C.S.Burner 🪙 ━►🔥GIT Node
Node / reticulum_mirror / MeshChatX / files / meshchatx / src / frontend / components / interfaces / AddInterfacePage.vue
Displaying Raw • Download
meshchatx/src/frontend/components/interfaces/AddInterfacePage.vue 9ec2a88817819d433c5d68dbec0ca070ca989b91 (9ec2a888) Text, 152.58 KB
T8b949e<!-- SPDX-License-Identifier: 0BSD AND MIT -->
Tff7b72<templateTff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex flex-col flex-1 overflow-hidden min-w-0 bg-slate-50 dark:bg-zinc-950Ta5d6ff"Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffoverflow-y-auto flex-1 min-h-0Ta5d6ff"Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffw-full max-w-[1920px] mx-auto px-4 md:px-5 lg:px-8 py-4 md:py-6 lg:py-8 space-y-6Ta5d6ff"Tff7b72>
T8b949e<!-- Header Section -->
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex flex-col sm:flex-row sm:items-center justify-between gap-4Ta5d6ff"Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffmin-w-0Ta5d6ff"Tff7b72>
Tff7b72<h1 Te6edf3class=Ta5d6ff"Ta5d6fftext-2xl font-bold text-gray-900 dark:text-white flex items-center gap-2Ta5d6ff"Tff7b72>
Tff7b72<MaterialDesignIcon
Te6edf3:icon-name=Ta5d6ff"Ta5d6ffisEditingInterface ? Ta5d6ff'Ta5d6ffpencilTa5d6ff'Ta5d6ff : Ta5d6ff'Ta5d6ffplus-circle-outlineTa5d6ff'Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffsize-7 text-blue-500Ta5d6ff"
Tff7b72/>
{{ isEditingInterface ? Te6edf3$t("interfaces.edit_interface") : Te6edf3$t("interfaces.add_interface") }}
Tff7b72</h1>
Tff7b72<p Te6edf3class=Ta5d6ff"Ta5d6fftext-sm text-gray-600 dark:text-zinc-400 mt-1Ta5d6ff"Tff7b72>
{{
isEditingInterface
? "Update existing connection settings."
: "Create a new connection to the Reticulum network."
}}
Tff7b72</p>
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex gap-2 shrink-0Ta5d6ff"Tff7b72>
Tff7b72<RouterLink Te6edf3:to=Ta5d6ff"Ta5d6ff{ name: Ta5d6ff'Ta5d6ffinterfacesTa5d6ff'Ta5d6ff }Ta5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffsecondary-chipTa5d6ff"Tff7b72>
Tff7b72<MaterialDesignIcon Te6edf3icon-name=Ta5d6ff"Ta5d6ffarrow-leftTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffsize-4Ta5d6ff" Tff7b72/>
Back to List
Tff7b72</RouterLink>
Tff7b72</div>
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex flex-col-reverse gap-8 xl:flex-row xl:items-start xl:gap-10Ta5d6ff"Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex-1 min-w-0 space-y-6 xl:max-w-4xl 2xl:max-w-5xlTa5d6ff"Tff7b72>
T8b949e<!-- Main Form Card -->
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffglass-card space-y-8Ta5d6ff"Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffgrid grid-cols-1 lg:grid-cols-2 gap-8Ta5d6ff"Tff7b72>
T8b949e<!-- Basic Info Column -->
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffspace-y-6Ta5d6ff"Tff7b72>
Tff7b72<div
Te6edf3class=Ta5d6ff"Ta5d6ffflex items-center gap-2 pb-2 border-b border-gray-100 dark:border-zinc-800Ta5d6ff"
Tff7b72>
Tff7b72<MaterialDesignIcon
Te6edf3icon-name=Ta5d6ff"Ta5d6ffinformation-outlineTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffsize-5 text-gray-400Ta5d6ff"
Tff7b72/>
Tff7b72<h3 Te6edf3class=Ta5d6ff"Ta5d6fffont-bold text-gray-900 dark:text-whiteTa5d6ff"Tff7b72>Basic ConfigurationTff7b72</h3>
Tff7b72</div>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Interface NameTff7b72</FormLabel>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceNameTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6fftextTa5d6ff"
Te6edf3:disabled=Ta5d6ff"Ta5d6ffisEditingInterfaceTa5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ffe.g. Home Node or Mobile TCPTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Te6edf3:class=Ta5d6ff"Ta5d6ff[isEditingInterface ? Ta5d6ff'Ta5d6ffcursor-not-allowed opacity-60Ta5d6ff'Ta5d6ff : Ta5d6ff'Ta5d6ff'Ta5d6ff]Ta5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Transport TypeTff7b72</FormLabel>
T8b949e<!-- Visual Transport Selection -->
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffgrid grid-cols-2 gap-2Ta5d6ff"Tff7b72>
Tff7b72<button
Te6edf3v-for=Ta5d6ff"Ta5d6fftype in [
{
id: Ta5d6ff'Ta5d6ffTCPClientInterfaceTa5d6ff'Ta5d6ff,
name: Ta5d6ff'Ta5d6ffTCP ClientTa5d6ff'Ta5d6ff,
icon: Ta5d6ff'Ta5d6fflan-connectTa5d6ff'Ta5d6ff,
color: Ta5d6ff'Ta5d6fftext-blue-500Ta5d6ff'Ta5d6ff,
},
{
id: Ta5d6ff'Ta5d6ffBackboneInterfaceTa5d6ff'Ta5d6ff,
name: Ta5d6ff'Ta5d6ffBackboneTa5d6ff'Ta5d6ff,
icon: Ta5d6ff'Ta5d6fftransit-connection-variantTa5d6ff'Ta5d6ff,
color: Ta5d6ff'Ta5d6fftext-sky-500Ta5d6ff'Ta5d6ff,
},
{
id: Ta5d6ff'Ta5d6ffTCPServerInterfaceTa5d6ff'Ta5d6ff,
name: Ta5d6ff'Ta5d6ffTCP ServerTa5d6ff'Ta5d6ff,
icon: Ta5d6ff'Ta5d6ffserver-networkTa5d6ff'Ta5d6ff,
color: Ta5d6ff'Ta5d6fftext-indigo-500Ta5d6ff'Ta5d6ff,
},
{
id: Ta5d6ff'Ta5d6ffUDPInterfaceTa5d6ff'Ta5d6ff,
name: Ta5d6ff'Ta5d6ffUDPTa5d6ff'Ta5d6ff,
icon: Ta5d6ff'Ta5d6ffbroadcastTa5d6ff'Ta5d6ff,
color: Ta5d6ff'Ta5d6fftext-cyan-500Ta5d6ff'Ta5d6ff,
},
{
id: Ta5d6ff'Ta5d6ffRNodeInterfaceTa5d6ff'Ta5d6ff,
name: Ta5d6ff'Ta5d6ffRNode (LoRa)Ta5d6ff'Ta5d6ff,
icon: Ta5d6ff'Ta5d6ffradio-handheldTa5d6ff'Ta5d6ff,
color: Ta5d6ff'Ta5d6fftext-emerald-500Ta5d6ff'Ta5d6ff,
},
{
id: Ta5d6ff'Ta5d6ffI2PInterfaceTa5d6ff'Ta5d6ff,
name: Ta5d6ff'Ta5d6ffI2P TunnelTa5d6ff'Ta5d6ff,
icon: Ta5d6ff'Ta5d6fftunnelTa5d6ff'Ta5d6ff,
color: Ta5d6ff'Ta5d6fftext-purple-500Ta5d6ff'Ta5d6ff,
},
{
id: Ta5d6ff'Ta5d6ffSerialInterfaceTa5d6ff'Ta5d6ff,
name: Ta5d6ff'Ta5d6ffSerial (Generic)Ta5d6ff'Ta5d6ff,
icon: Ta5d6ff'Ta5d6ffserial-portTa5d6ff'Ta5d6ff,
color: Ta5d6ff'Ta5d6fftext-amber-500Ta5d6ff'Ta5d6ff,
},
{
id: Ta5d6ff'Ta5d6ffKISSInterfaceTa5d6ff'Ta5d6ff,
name: Ta5d6ff'Ta5d6ffKISS (TNC)Ta5d6ff'Ta5d6ff,
icon: Ta5d6ff'Ta5d6ffradio-towerTa5d6ff'Ta5d6ff,
color: Ta5d6ff'Ta5d6fftext-orange-500Ta5d6ff'Ta5d6ff,
},
{
id: Ta5d6ff'Ta5d6ffAutoInterfaceTa5d6ff'Ta5d6ff,
name: Ta5d6ff'Ta5d6ffAuto (Local)Ta5d6ff'Ta5d6ff,
icon: Ta5d6ff'Ta5d6ffauto-fixTa5d6ff'Ta5d6ff,
color: Ta5d6ff'Ta5d6fftext-pink-500Ta5d6ff'Ta5d6ff,
},
]Ta5d6ff"
Te6edf3:key=Ta5d6ff"Ta5d6fftype.idTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6ffbuttonTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffflex flex-col items-center justify-center p-3 rounded-2xl border transition-all duration-200 text-center gap-1 groupTa5d6ff"
Te6edf3:class=Ta5d6ff"Ta5d6ff[
newInterfaceType === type.id
? Ta5d6ff'Ta5d6ffbg-blue-500/10 border-blue-500 ring-1 ring-blue-500/50Ta5d6ff'
Ta5d6ff : Ta5d6ff'Ta5d6ffbg-gray-50/50 dark:bg-zinc-800/30 border-gray-100 dark:border-zinc-800 hover:border-gray-300 dark:hover:border-zinc-600Ta5d6ff'Ta5d6ff,
]Ta5d6ff"
@Te6edf3click=Ta5d6ff"Ta5d6ffnewInterfaceType = type.idTa5d6ff"
Tff7b72>
Tff7b72<MaterialDesignIcon
Te6edf3:icon-name=Ta5d6ff"Ta5d6fftype.iconTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffsize-6 transition-transform group-hover:scale-110Ta5d6ff"
Te6edf3:class=Ta5d6ff"Ta5d6ff[
newInterfaceType === type.id ? Ta5d6ff'Ta5d6fftext-blue-500Ta5d6ff'Ta5d6ff : type.color,
]Ta5d6ff"
Tff7b72/>
Tff7b72<span
Te6edf3class=Ta5d6ff"Ta5d6fftext-[10px] font-bold uppercase tracking-tightTa5d6ff"
Te6edf3:class=Ta5d6ff"Ta5d6ff[
newInterfaceType === type.id
? Ta5d6ff'Ta5d6fftext-blue-700 dark:text-blue-400Ta5d6ff'
Ta5d6ff : Ta5d6ff'Ta5d6fftext-gray-600 dark:text-zinc-400Ta5d6ff'Ta5d6ff,
]Ta5d6ff"
Tff7b72>
{{ type.name }}
Tff7b72</span>
Tff7b72</button>
Tff7b72</div>
T8b949e<!-- Fallback/More select for less common types -->
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffmt-3Ta5d6ff"Tff7b72>
Tff7b72<select
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceTypeTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-field appearance-none pr-10 !py-1.5 !text-[11px] opacity-70 hover:opacity-100Ta5d6ff"
Tff7b72>
Tff7b72<option Te6edf3:value=Ta5d6ff"Ta5d6ffnullTa5d6ff"Tff7b72>More options...Tff7b72</option>
Tff7b72<option Te6edf3value=Ta5d6ff"Ta5d6ffAX25KISSInterfaceTa5d6ff"Tff7b72>AX.25 KISS (Amateur Radio)Tff7b72</option>
Tff7b72<option Te6edf3value=Ta5d6ff"Ta5d6ffLocalInterfaceTa5d6ff"Tff7b72>Local Interface (Loopback)Tff7b72</option>
Tff7b72<option Te6edf3value=Ta5d6ff"Ta5d6ffPipeInterfaceTa5d6ff"Tff7b72>Pipe Interface (External)Tff7b72</option>
Tff7b72<option Te6edf3value=Ta5d6ff"Ta5d6ffRNodeIPInterfaceTa5d6ff"Tff7b72>RNode over IPTff7b72</option>
Tff7b72<option Te6edf3value=Ta5d6ff"Ta5d6ffBackboneInterfaceTa5d6ff"Tff7b72>Backbone (public relay)Tff7b72</option>
Tff7b72</select>
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>
T8b949e<!-- Dynamic Interface Specific Settings Column -->
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffspace-y-6Ta5d6ff"Tff7b72>
Tff7b72<div
Te6edf3class=Ta5d6ff"Ta5d6ffflex items-center gap-2 pb-2 border-b border-gray-100 dark:border-zinc-800Ta5d6ff"
Tff7b72>
Tff7b72<MaterialDesignIcon Te6edf3icon-name=Ta5d6ff"Ta5d6ffcog-outlineTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffsize-5 text-gray-400Ta5d6ff" Tff7b72/>
Tff7b72<h3 Te6edf3class=Ta5d6ff"Ta5d6fffont-bold text-gray-900 dark:text-whiteTa5d6ff"Tff7b72>Connection DetailsTff7b72</h3>
Tff7b72</div>
T8b949e<!-- No selection placeholder -->
Tff7b72<div
Te6edf3v-if=Ta5d6ff"Ta5d6ff!newInterfaceTypeTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffh-48 flex flex-col items-center justify-center text-center p-6 border-2 border-dashed border-gray-100 dark:border-zinc-800 rounded-3xlTa5d6ff"
Tff7b72>
Tff7b72<MaterialDesignIcon
Te6edf3icon-name=Ta5d6ff"Ta5d6ffarrow-left-boldTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffsize-10 text-gray-200 dark:text-zinc-800 animate-bounce-leftTa5d6ff"
Tff7b72/>
Tff7b72<p Te6edf3class=Ta5d6ff"Ta5d6fftext-sm text-gray-400 dark:text-zinc-600 mt-2Ta5d6ff"Tff7b72>
Select an interface type to configure connection settings.
Tff7b72</p>
Tff7b72</div>
T8b949e<!-- Interface Specific Fields -->
Tff7b72<div v-else Te6edf3class=Ta5d6ff"Ta5d6ffspace-y-6 animate-in fade-in slide-in-from-right-4 duration-300Ta5d6ff"Tff7b72>
T8b949e<!-- TCP Client -->
Tff7b72<div Te6edf3v-if=Ta5d6ff"Ta5d6ffnewInterfaceType === Ta5d6ff'Ta5d6ffTCPClientInterfaceTa5d6ff'Ta5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffspace-y-4Ta5d6ff"Tff7b72>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Target HostTff7b72</FormLabel>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceTargetHostTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6fftextTa5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ffe.g. 1.2.3.4 or example.comTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Target PortTff7b72</FormLabel>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceTargetPortTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6ffnumberTa5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ff4242Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex items-center gap-2Ta5d6ff"Tff7b72>
Tff7b72<Toggle
Te6edf3id=Ta5d6ff"Ta5d6fftcp-kiss-framingTa5d6ff"
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceKISSFramingEnabledTa5d6ff"
Tff7b72/>
Tff7b72<FormLabel Te6edf3for=Ta5d6ff"Ta5d6fftcp-kiss-framingTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffcursor-pointer !mb-0 text-smTa5d6ff"
Tff7b72>Use KISS framing (legacy compatibility)Tff7b72</FormLabel
>
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex items-center gap-2Ta5d6ff"Tff7b72>
Tff7b72<Toggle
Te6edf3id=Ta5d6ff"Ta5d6fftcp-i2p-tunneledTa5d6ff"
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceI2PTunnelingEnabledTa5d6ff"
Tff7b72/>
Tff7b72<FormLabel Te6edf3for=Ta5d6ff"Ta5d6fftcp-i2p-tunneledTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffcursor-pointer !mb-0 text-smTa5d6ff"
Tff7b72>I2P Tunneled (target is an I2P b32)Tff7b72</FormLabel
>
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffgrid grid-cols-3 gap-3Ta5d6ff"Tff7b72>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Connect Timeout (s)Tff7b72</FormLabel>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceConnectTimeoutTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6ffnumberTa5d6ff"
Te6edf3min=Ta5d6ff"Ta5d6ff0Ta5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ffdefaultTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Max Reconnect TriesTff7b72</FormLabel>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceMaxReconnectTriesTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6ffnumberTa5d6ff"
Te6edf3min=Ta5d6ff"Ta5d6ff0Ta5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ffdefaultTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Fixed MTUTff7b72</FormLabel>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceFixedMTUTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6ffnumberTa5d6ff"
Te6edf3min=Ta5d6ff"Ta5d6ff0Ta5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ffautoTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>
Tff7b72<div Te6edf3v-if=Ta5d6ff"Ta5d6ffnewInterfaceType === Ta5d6ff'Ta5d6ffBackboneInterfaceTa5d6ff'Ta5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffspace-y-4Ta5d6ff"Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex items-center gap-2Ta5d6ff"Tff7b72>
Tff7b72<Toggle
Te6edf3id=Ta5d6ff"Ta5d6ffbackbone-listen-modeTa5d6ff"
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceBackboneListenModeTa5d6ff"
Tff7b72/>
Tff7b72<FormLabel
Te6edf3for=Ta5d6ff"Ta5d6ffbackbone-listen-modeTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffcursor-pointer !mb-0 text-smTa5d6ff"
Tff7b72>Listener mode (host this backbone)Tff7b72</FormLabel
>
Tff7b72</div>
Tff7b72<div Te6edf3v-if=Ta5d6ff"Ta5d6ff!newInterfaceBackboneListenModeTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffspace-y-4Ta5d6ff"Tff7b72>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Remote hostTff7b72</FormLabel>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceTargetHostTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6fftextTa5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ffe.g. example.com or 1.2.3.4Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Target portTff7b72</FormLabel>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceTargetPortTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6ffnumberTa5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ff4242Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Transport identity (hex)Tff7b72</FormLabel>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceTransportIdentityTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6fftextTa5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ff32 hex chars from the directoryTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-field font-mono text-xsTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72</div>
Tff7b72<div v-else Te6edf3class=Ta5d6ff"Ta5d6ffspace-y-4Ta5d6ff"Tff7b72>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Listen IP (Optional)Tff7b72</FormLabel>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceBackboneListenIpTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6fftextTa5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ff0.0.0.0Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Listen PortTff7b72</FormLabel>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceBackboneListenPortTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6ffnumberTa5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ff4242Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Device (optional)Tff7b72</FormLabel>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceBackboneListenDeviceTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6fftextTa5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ffe.g. eth0Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex items-center gap-2Ta5d6ff"Tff7b72>
Tff7b72<Toggle
Te6edf3id=Ta5d6ff"Ta5d6ffbackbone-listen-ipv6Ta5d6ff"
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfacePreferIPV6Ta5d6ff"
Tff7b72/>
Tff7b72<FormLabel
Te6edf3for=Ta5d6ff"Ta5d6ffbackbone-listen-ipv6Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffcursor-pointer !mb-0 text-smTa5d6ff"
Tff7b72>Prefer IPv6Tff7b72</FormLabel
>
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>
T8b949e<!-- TCP Server / UDP -->
Tff7b72<div
Te6edf3v-if=Ta5d6ff"Ta5d6ff[Ta5d6ff'Ta5d6ffTCPServerInterfaceTa5d6ff'Ta5d6ff, Ta5d6ff'Ta5d6ffUDPInterfaceTa5d6ff'Ta5d6ff].includes(newInterfaceType)Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffspace-y-4Ta5d6ff"
Tff7b72>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Listen IP (Optional)Tff7b72</FormLabel>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceListenIpTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6fftextTa5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ff0.0.0.0Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Listen PortTff7b72</FormLabel>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceListenPortTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6ffnumberTa5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ff4242Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Device (optional)Tff7b72</FormLabel>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceNetworkDeviceTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6fftextTa5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ffe.g. eth0 (overrides Listen IP)Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72<div
Te6edf3v-if=Ta5d6ff"Ta5d6ffnewInterfaceType === Ta5d6ff'Ta5d6ffTCPServerInterfaceTa5d6ff'Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffflex flex-wrap items-center gap-4Ta5d6ff"
Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex items-center gap-2Ta5d6ff"Tff7b72>
Tff7b72<Toggle Te6edf3id=Ta5d6ff"Ta5d6fftcp-server-ipv6Ta5d6ff" Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfacePreferIPV6Ta5d6ff" Tff7b72/>
Tff7b72<FormLabel
Te6edf3for=Ta5d6ff"Ta5d6fftcp-server-ipv6Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffcursor-pointer !mb-0 text-smTa5d6ff"
Tff7b72>Prefer IPv6Tff7b72</FormLabel
>
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex items-center gap-2Ta5d6ff"Tff7b72>
Tff7b72<Toggle
Te6edf3id=Ta5d6ff"Ta5d6fftcp-server-i2pTa5d6ff"
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceI2PTunnelingEnabledTa5d6ff"
Tff7b72/>
Tff7b72<FormLabel Te6edf3for=Ta5d6ff"Ta5d6fftcp-server-i2pTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffcursor-pointer !mb-0 text-smTa5d6ff"
Tff7b72>I2P TunneledTff7b72</FormLabel
>
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>
T8b949e<!-- UDP Extras -->
Tff7b72<div Te6edf3v-if=Ta5d6ff"Ta5d6ffnewInterfaceType === Ta5d6ff'Ta5d6ffUDPInterfaceTa5d6ff'Ta5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffgrid grid-cols-2 gap-4Ta5d6ff"Tff7b72>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Forward IPTff7b72</FormLabel>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceForwardIpTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6fftextTa5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ff255.255.255.255Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Forward PortTff7b72</FormLabel>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceForwardPortTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6ffnumberTa5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ff4242Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72</div>
T8b949e<!-- I2P Interface -->
Tff7b72<div Te6edf3v-if=Ta5d6ff"Ta5d6ffnewInterfaceType === Ta5d6ff'Ta5d6ffI2PInterfaceTa5d6ff'Ta5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffspace-y-4Ta5d6ff"Tff7b72>
Tff7b72<div
Te6edf3class=Ta5d6ff"Ta5d6ffbg-blue-50/50 dark:bg-blue-900/10 p-3 rounded-2xl border border-blue-100 dark:border-blue-900/20 text-xs text-blue-800 dark:text-blue-300Ta5d6ff"
Tff7b72>
ⓘ To use the I2P interface, you must have an I2P router running on your
system.
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex items-center gap-2Ta5d6ff"Tff7b72>
Tff7b72<Toggle Te6edf3id=Ta5d6ff"Ta5d6ffi2p-connectableTa5d6ff" Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceConnectableTa5d6ff" Tff7b72/>
Tff7b72<FormLabel Te6edf3for=Ta5d6ff"Ta5d6ffi2p-connectableTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffcursor-pointer !mb-0 text-smTa5d6ff"
Tff7b72>Allow incoming peers (connectable)Tff7b72</FormLabel
>
Tff7b72</div>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Initial Peers (Optional)Tff7b72</FormLabel>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffspace-y-2Ta5d6ff"Tff7b72>
Tff7b72<div
Te6edf3v-for=Ta5d6ff"Ta5d6ff(peer, index) in I2PSettings.newInterfacePeersTa5d6ff"
Te6edf3:key=Ta5d6ff"Ta5d6ffindexTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffflex items-center gap-2Ta5d6ff"
Tff7b72>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffI2PSettings.newInterfacePeers[index]Ta5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6fftextTa5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ffb32.i2p addressTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72<button
Te6edf3type=Ta5d6ff"Ta5d6ffbuttonTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6fftext-red-500 hover:text-red-400 p-1Ta5d6ff"
@Te6edf3click=Ta5d6ff"Ta5d6ffremoveI2PPeer(index)Ta5d6ff"
Tff7b72>
Tff7b72<MaterialDesignIcon
Te6edf3icon-name=Ta5d6ff"Ta5d6fftrash-can-outlineTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffsize-5Ta5d6ff"
Tff7b72/>
Tff7b72</button>
Tff7b72</div>
Tff7b72<button
Te6edf3type=Ta5d6ff"Ta5d6ffbuttonTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffsecondary-chip !py-1 !px-3 !text-[10px]Ta5d6ff"
@Te6edf3click=Ta5d6ff"Ta5d6ffaddI2PPeer(Ta5d6ff'Ta5d6ff'Ta5d6ff)Ta5d6ff"
Tff7b72>
Tff7b72<MaterialDesignIcon Te6edf3icon-name=Ta5d6ff"Ta5d6ffplusTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffsize-3Ta5d6ff" Tff7b72/> Add Peer
Tff7b72</button>
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>
T8b949e<!-- RNode / Hardware -->
Tff7b72<div
Te6edf3v-if=Ta5d6ff"
Ta5d6ff [
Ta5d6ff'Ta5d6ffRNodeInterfaceTa5d6ff'Ta5d6ff,
Ta5d6ff'Ta5d6ffRNodeIPInterfaceTa5d6ff'Ta5d6ff,
Ta5d6ff'Ta5d6ffSerialInterfaceTa5d6ff'Ta5d6ff,
Ta5d6ff'Ta5d6ffKISSInterfaceTa5d6ff'Ta5d6ff,
Ta5d6ff'Ta5d6ffAX25KISSInterfaceTa5d6ff'Ta5d6ff,
].includes(newInterfaceType)
Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffspace-y-4Ta5d6ff"
Tff7b72>
Tff7b72<div
Te6edf3v-if=Ta5d6ff"Ta5d6ffnewInterfaceType === Ta5d6ff'Ta5d6ffRNodeInterfaceTa5d6ff'Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffflex items-center gap-2 pb-2Ta5d6ff"
Tff7b72>
Tff7b72<Toggle Te6edf3id=Ta5d6ff"Ta5d6ffrnode-use-ipTa5d6ff" Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceRNodeUseIPTa5d6ff" Tff7b72/>
Tff7b72<FormLabel Te6edf3for=Ta5d6ff"Ta5d6ffrnode-use-ipTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffcursor-pointer !mb-0 text-smTa5d6ff"
Tff7b72>Connect over network (IP)Tff7b72</FormLabel
>
Tff7b72</div>
Tff7b72<div
Te6edf3v-if=Ta5d6ff"Ta5d6ffnewInterfaceRNodeUseIP || newInterfaceType === Ta5d6ff'Ta5d6ffRNodeIPInterfaceTa5d6ff'Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffgrid grid-cols-2 gap-4Ta5d6ff"
Tff7b72>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>HostTff7b72</FormLabel>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceRNodeIPHostTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6fftextTa5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ff10.0.0.1Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>PortTff7b72</FormLabel>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceRNodeIPPortTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6ffnumberTa5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ff7633Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72</div>
Tff7b72<div v-elseTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Serial PortTff7b72</FormLabel>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffrelativeTa5d6ff"Tff7b72>
Tff7b72<select
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfacePortTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-field appearance-none pr-10Ta5d6ff"
Tff7b72>
Tff7b72<option Te6edf3:value=Ta5d6ff"Ta5d6ffnullTa5d6ff" disabledTff7b72>Select a port...Tff7b72</option>
Tff7b72<option
Te6edf3v-for=Ta5d6ff"Ta5d6ffport in comportsTa5d6ff"
Te6edf3:key=Ta5d6ff"Ta5d6ffport.deviceTa5d6ff"
Te6edf3:value=Ta5d6ff"Ta5d6ffport.deviceTa5d6ff"
Tff7b72>
{{ port.device }} ({{ port.product ?? "?" }})
Tff7b72</option>
Tff7b72</select>
Tff7b72<div
Te6edf3class=Ta5d6ff"Ta5d6ffabsolute inset-y-0 right-0 flex items-center px-3 pointer-events-none text-gray-400Ta5d6ff"
Tff7b72>
Tff7b72<MaterialDesignIcon Te6edf3icon-name=Ta5d6ff"Ta5d6ffchevron-downTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffsize-5Ta5d6ff" Tff7b72/>
Tff7b72</div>
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffmt-2 flex justify-endTa5d6ff"Tff7b72>
Tff7b72<button
Te6edf3type=Ta5d6ff"Ta5d6ffbuttonTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6fftext-[10px] uppercase font-bold text-blue-500 hover:text-blue-600 tracking-widerTa5d6ff"
@Te6edf3click=Ta5d6ff"Ta5d6ffloadComportsTa5d6ff"
Tff7b72>
Refresh Ports
Tff7b72</button>
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>
T8b949e<!-- RNode Radio Parameters -->
Tff7b72<div
Te6edf3v-if=Ta5d6ff"Ta5d6ff[Ta5d6ff'Ta5d6ffRNodeInterfaceTa5d6ff'Ta5d6ff, Ta5d6ff'Ta5d6ffRNodeIPInterfaceTa5d6ff'Ta5d6ff].includes(newInterfaceType)Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffspace-y-4 pt-4 border-t border-gray-100 dark:border-zinc-800Ta5d6ff"
Tff7b72>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Frequency (Hz)Tff7b72</FormLabel>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex items-center gap-2Ta5d6ff"Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex-1Ta5d6ff"Tff7b72>
Tff7b72<input
v-model.Te6edf3number=Ta5d6ff"Ta5d6ffRNodeGHzValueTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6ffnumberTa5d6ff"
Te6edf3min=Ta5d6ff"Ta5d6ff0Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-field text-centerTa5d6ff"
Tff7b72/>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6fftext-[10px] text-center text-gray-400 mt-1Ta5d6ff"Tff7b72>
GHz
Tff7b72</div>
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex-1Ta5d6ff"Tff7b72>
Tff7b72<input
v-model.Te6edf3number=Ta5d6ff"Ta5d6ffRNodeMHzValueTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6ffnumberTa5d6ff"
Te6edf3min=Ta5d6ff"Ta5d6ff0Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-field text-centerTa5d6ff"
Tff7b72/>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6fftext-[10px] text-center text-gray-400 mt-1Ta5d6ff"Tff7b72>
MHz
Tff7b72</div>
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex-1Ta5d6ff"Tff7b72>
Tff7b72<input
v-model.Te6edf3number=Ta5d6ff"Ta5d6ffRNodekHzValueTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6ffnumberTa5d6ff"
Te6edf3min=Ta5d6ff"Ta5d6ff0Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-field text-centerTa5d6ff"
Tff7b72/>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6fftext-[10px] text-center text-gray-400 mt-1Ta5d6ff"Tff7b72>
kHz
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>
Tff7b72<div
Te6edf3v-if=Ta5d6ff"Ta5d6ffformattedFrequencyTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffmt-2 text-center text-sm font-mono text-blue-500 font-boldTa5d6ff"
Tff7b72>
{{ formattedFrequency }}
Tff7b72</div>
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffgrid grid-cols-2 gap-4Ta5d6ff"Tff7b72>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>BandwidthTff7b72</FormLabel>
Tff7b72<select Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceBandwidthTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"Tff7b72>
Tff7b72<option
Te6edf3v-for=Ta5d6ff"Ta5d6ffbw in RNodeInterfaceDefaults.bandwidthsTa5d6ff"
Te6edf3:key=Ta5d6ff"Ta5d6ffbwTa5d6ff"
Te6edf3:value=Ta5d6ff"Ta5d6ffbwTa5d6ff"
Tff7b72>
{{ bw / 1000 }} kHz
Tff7b72</option>
Tff7b72</select>
Tff7b72</div>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Power (dBm)Tff7b72</FormLabel>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceTxpowerTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6ffnumberTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffgrid grid-cols-2 gap-4Ta5d6ff"Tff7b72>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Spreading FactorTff7b72</FormLabel>
Tff7b72<select Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceSpreadingFactorTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"Tff7b72>
Tff7b72<option
Te6edf3v-for=Ta5d6ff"Ta5d6ffsf in RNodeInterfaceDefaults.spreadingfactorsTa5d6ff"
Te6edf3:key=Ta5d6ff"Ta5d6ffsfTa5d6ff"
Te6edf3:value=Ta5d6ff"Ta5d6ffsfTa5d6ff"
Tff7b72>
SF{{ sf }}
Tff7b72</option>
Tff7b72</select>
Tff7b72</div>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Coding RateTff7b72</FormLabel>
Tff7b72<select Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceCodingRateTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"Tff7b72>
Tff7b72<option
Te6edf3v-for=Ta5d6ff"Ta5d6ffcr in RNodeInterfaceDefaults.codingratesTa5d6ff"
Te6edf3:key=Ta5d6ff"Ta5d6ffcrTa5d6ff"
Te6edf3:value=Ta5d6ff"Ta5d6ffcrTa5d6ff"
Tff7b72>
4:{{ cr }}
Tff7b72</option>
Tff7b72</select>
Tff7b72</div>
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex items-center gap-2Ta5d6ff"Tff7b72>
Tff7b72<Toggle Te6edf3id=Ta5d6ff"Ta5d6ffrnode-flow-controlTa5d6ff" Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceFlowControlTa5d6ff" Tff7b72/>
Tff7b72<FormLabel Te6edf3for=Ta5d6ff"Ta5d6ffrnode-flow-controlTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffcursor-pointer !mb-0 text-smTa5d6ff"
Tff7b72>Hardware flow controlTff7b72</FormLabel
>
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffgrid grid-cols-2 gap-4Ta5d6ff"Tff7b72>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Station ID CallsignTff7b72</FormLabel>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceIDCallsignTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6fftextTa5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ffe.g. NOCALLTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>ID Interval (s)Tff7b72</FormLabel>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceIDIntervalTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6ffnumberTa5d6ff"
Te6edf3min=Ta5d6ff"Ta5d6ff0Ta5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ff600Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffgrid grid-cols-2 gap-4Ta5d6ff"Tff7b72>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Airtime Limit Long (%)Tff7b72</FormLabel>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceAirtimeLimitLongTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6ffnumberTa5d6ff"
Te6edf3min=Ta5d6ff"Ta5d6ff0Ta5d6ff"
Te6edf3max=Ta5d6ff"Ta5d6ff100Ta5d6ff"
Te6edf3step=Ta5d6ff"Ta5d6ff0.1Ta5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ffoptionalTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Airtime Limit Short (%)Tff7b72</FormLabel>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceAirtimeLimitShortTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6ffnumberTa5d6ff"
Te6edf3min=Ta5d6ff"Ta5d6ff0Ta5d6ff"
Te6edf3max=Ta5d6ff"Ta5d6ff100Ta5d6ff"
Te6edf3step=Ta5d6ff"Ta5d6ff0.1Ta5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ffoptionalTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>
T8b949e<!-- Serial port settings (Serial/KISS/AX25KISS) -->
Tff7b72<div
Te6edf3v-if=Ta5d6ff"
Ta5d6ff [Ta5d6ff'Ta5d6ffSerialInterfaceTa5d6ff'Ta5d6ff, Ta5d6ff'Ta5d6ffKISSInterfaceTa5d6ff'Ta5d6ff, Ta5d6ff'Ta5d6ffAX25KISSInterfaceTa5d6ff'Ta5d6ff].includes(
newInterfaceType
)
Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffspace-y-4 pt-4 border-t border-gray-100 dark:border-zinc-800Ta5d6ff"
Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffgrid grid-cols-2 gap-4Ta5d6ff"Tff7b72>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Baud RateTff7b72</FormLabel>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceSpeedTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6ffnumberTa5d6ff"
Te6edf3min=Ta5d6ff"Ta5d6ff0Ta5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ff9600Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Data BitsTff7b72</FormLabel>
Tff7b72<select Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceDatabitsTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"Tff7b72>
Tff7b72<option Te6edf3:value=Ta5d6ff"Ta5d6ffnullTa5d6ff"Tff7b72>Default (8)Tff7b72</option>
Tff7b72<option Te6edf3:value=Ta5d6ff"Ta5d6ff5Ta5d6ff"Tff7b72>5Tff7b72</option>
Tff7b72<option Te6edf3:value=Ta5d6ff"Ta5d6ff6Ta5d6ff"Tff7b72>6Tff7b72</option>
Tff7b72<option Te6edf3:value=Ta5d6ff"Ta5d6ff7Ta5d6ff"Tff7b72>7Tff7b72</option>
Tff7b72<option Te6edf3:value=Ta5d6ff"Ta5d6ff8Ta5d6ff"Tff7b72>8Tff7b72</option>
Tff7b72</select>
Tff7b72</div>
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffgrid grid-cols-2 gap-4Ta5d6ff"Tff7b72>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>ParityTff7b72</FormLabel>
Tff7b72<select Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceParityTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"Tff7b72>
Tff7b72<option Te6edf3:value=Ta5d6ff"Ta5d6ffnullTa5d6ff"Tff7b72>Default (none)Tff7b72</option>
Tff7b72<option Te6edf3value=Ta5d6ff"Ta5d6ffNTa5d6ff"Tff7b72>NoneTff7b72</option>
Tff7b72<option Te6edf3value=Ta5d6ff"Ta5d6ffETa5d6ff"Tff7b72>EvenTff7b72</option>
Tff7b72<option Te6edf3value=Ta5d6ff"Ta5d6ffOTa5d6ff"Tff7b72>OddTff7b72</option>
Tff7b72</select>
Tff7b72</div>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Stop BitsTff7b72</FormLabel>
Tff7b72<select Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceStopbitsTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"Tff7b72>
Tff7b72<option Te6edf3:value=Ta5d6ff"Ta5d6ffnullTa5d6ff"Tff7b72>Default (1)Tff7b72</option>
Tff7b72<option Te6edf3:value=Ta5d6ff"Ta5d6ff1Ta5d6ff"Tff7b72>1Tff7b72</option>
Tff7b72<option Te6edf3:value=Ta5d6ff"Ta5d6ff2Ta5d6ff"Tff7b72>2Tff7b72</option>
Tff7b72</select>
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>
T8b949e<!-- KISS framing parameters (KISS/AX25KISS) -->
Tff7b72<div
Te6edf3v-if=Ta5d6ff"Ta5d6ff[Ta5d6ff'Ta5d6ffKISSInterfaceTa5d6ff'Ta5d6ff, Ta5d6ff'Ta5d6ffAX25KISSInterfaceTa5d6ff'Ta5d6ff].includes(newInterfaceType)Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffspace-y-4 pt-4 border-t border-gray-100 dark:border-zinc-800Ta5d6ff"
Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffgrid grid-cols-2 gap-4Ta5d6ff"Tff7b72>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Preamble (ms)Tff7b72</FormLabel>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfacePreambleTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6ffnumberTa5d6ff"
Te6edf3min=Ta5d6ff"Ta5d6ff0Ta5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ff350Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>TX Tail (ms)Tff7b72</FormLabel>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceTXTailTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6ffnumberTa5d6ff"
Te6edf3min=Ta5d6ff"Ta5d6ff0Ta5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ff20Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Persistence (0-255)Tff7b72</FormLabel>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfacePersistenceTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6ffnumberTa5d6ff"
Te6edf3min=Ta5d6ff"Ta5d6ff0Ta5d6ff"
Te6edf3max=Ta5d6ff"Ta5d6ff255Ta5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ff64Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Slot Time (ms)Tff7b72</FormLabel>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceSlotTimeTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6ffnumberTa5d6ff"
Te6edf3min=Ta5d6ff"Ta5d6ff0Ta5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ff20Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex items-center gap-2Ta5d6ff"Tff7b72>
Tff7b72<Toggle Te6edf3id=Ta5d6ff"Ta5d6ffkiss-flow-controlTa5d6ff" Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceFlowControlTa5d6ff" Tff7b72/>
Tff7b72<FormLabel Te6edf3for=Ta5d6ff"Ta5d6ffkiss-flow-controlTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffcursor-pointer !mb-0 text-smTa5d6ff"
Tff7b72>Hardware flow controlTff7b72</FormLabel
>
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffgrid grid-cols-2 gap-4Ta5d6ff"Tff7b72>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Beacon CallsignTff7b72</FormLabel>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceIDCallsignTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6fftextTa5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ffoptionalTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Beacon Interval (s)Tff7b72</FormLabel>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceIDIntervalTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6ffnumberTa5d6ff"
Te6edf3min=Ta5d6ff"Ta5d6ff0Ta5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ffoptionalTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>
T8b949e<!-- AX.25 KISS extras -->
Tff7b72<div
Te6edf3v-if=Ta5d6ff"Ta5d6ffnewInterfaceType === Ta5d6ff'Ta5d6ffAX25KISSInterfaceTa5d6ff'Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffspace-y-4 pt-4 border-t border-gray-100 dark:border-zinc-800Ta5d6ff"
Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffgrid grid-cols-2 gap-4Ta5d6ff"Tff7b72>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>AX.25 CallsignTff7b72</FormLabel>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceCallsignTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6fftextTa5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ffe.g. NOCALLTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>SSID (0-15)Tff7b72</FormLabel>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceSSIDTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6ffnumberTa5d6ff"
Te6edf3min=Ta5d6ff"Ta5d6ff0Ta5d6ff"
Te6edf3max=Ta5d6ff"Ta5d6ff15Ta5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ff0Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>
T8b949e<!-- AutoInterface -->
Tff7b72<div Te6edf3v-if=Ta5d6ff"Ta5d6ffnewInterfaceType === Ta5d6ff'Ta5d6ffAutoInterfaceTa5d6ff'Ta5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffspace-y-4Ta5d6ff"Tff7b72>
Tff7b72<div
Te6edf3class=Ta5d6ff"Ta5d6ffbg-pink-50/50 dark:bg-pink-900/10 p-3 rounded-2xl border border-pink-100 dark:border-pink-900/20 text-xs text-pink-800 dark:text-pink-300Ta5d6ff"
Tff7b72>
ⓘ Auto Interface auto-discovers peers on connected networks via IPv6
multicast.
Tff7b72</div>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Group ID (optional)Tff7b72</FormLabel>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceGroupIDTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6fftextTa5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ffreticulumTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72<p Te6edf3class=Ta5d6ff"Ta5d6fftext-[10px] text-gray-400 mt-1Ta5d6ff"Tff7b72>
Only peers sharing the same group_id will discover each other.
Tff7b72</p>
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffgrid grid-cols-2 gap-4Ta5d6ff"Tff7b72>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Discovery ScopeTff7b72</FormLabel>
Tff7b72<select Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceDiscoveryScopeTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"Tff7b72>
Tff7b72<option Te6edf3:value=Ta5d6ff"Ta5d6ffnullTa5d6ff"Tff7b72>Default (link)Tff7b72</option>
Tff7b72<option Te6edf3value=Ta5d6ff"Ta5d6fflinkTa5d6ff"Tff7b72>Link (local segment)Tff7b72</option>
Tff7b72<option Te6edf3value=Ta5d6ff"Ta5d6ffadminTa5d6ff"Tff7b72>AdministrativeTff7b72</option>
Tff7b72<option Te6edf3value=Ta5d6ff"Ta5d6ffsiteTa5d6ff"Tff7b72>SiteTff7b72</option>
Tff7b72<option Te6edf3value=Ta5d6ff"Ta5d6fforganisationTa5d6ff"Tff7b72>OrganisationTff7b72</option>
Tff7b72<option Te6edf3value=Ta5d6ff"Ta5d6ffglobalTa5d6ff"Tff7b72>GlobalTff7b72</option>
Tff7b72</select>
Tff7b72</div>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Multicast Address TypeTff7b72</FormLabel>
Tff7b72<select
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceMulticastAddressTypeTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72>
Tff7b72<option Te6edf3:value=Ta5d6ff"Ta5d6ffnullTa5d6ff"Tff7b72>Default (temporary)Tff7b72</option>
Tff7b72<option Te6edf3value=Ta5d6ff"Ta5d6fftemporaryTa5d6ff"Tff7b72>TemporaryTff7b72</option>
Tff7b72<option Te6edf3value=Ta5d6ff"Ta5d6ffpermanentTa5d6ff"Tff7b72>PermanentTff7b72</option>
Tff7b72</select>
Tff7b72</div>
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffgrid grid-cols-2 gap-4Ta5d6ff"Tff7b72>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Discovery PortTff7b72</FormLabel>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceDiscoveryPortTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6ffnumberTa5d6ff"
Te6edf3min=Ta5d6ff"Ta5d6ff1Ta5d6ff"
Te6edf3max=Ta5d6ff"Ta5d6ff65535Ta5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ff29716Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Data PortTff7b72</FormLabel>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceDataPortTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6ffnumberTa5d6ff"
Te6edf3min=Ta5d6ff"Ta5d6ff1Ta5d6ff"
Te6edf3max=Ta5d6ff"Ta5d6ff65535Ta5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ff42671Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72</div>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"
Tff7b72>Allowed Devices (comma separated)Tff7b72</FormLabel
>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceDevicesTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6fftextTa5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ffe.g. eth0, wlan0Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"
Tff7b72>Ignored Devices (comma separated)Tff7b72</FormLabel
>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceIgnoredDevicesTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6fftextTa5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ffe.g. tun0, docker0Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Configured Bitrate (bps)Tff7b72</FormLabel>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceConfiguredBitrateTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6ffnumberTa5d6ff"
Te6edf3min=Ta5d6ff"Ta5d6ff0Ta5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ffoptional overrideTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72</div>
T8b949e<!-- Pipe Interface -->
Tff7b72<div Te6edf3v-if=Ta5d6ff"Ta5d6ffnewInterfaceType === Ta5d6ff'Ta5d6ffPipeInterfaceTa5d6ff'Ta5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffspace-y-4Ta5d6ff"Tff7b72>
Tff7b72<div
Te6edf3class=Ta5d6ff"Ta5d6ffbg-gray-50/50 dark:bg-zinc-800/30 p-3 rounded-2xl border border-gray-100 dark:border-zinc-800 text-xs text-gray-600 dark:text-zinc-400Ta5d6ff"
Tff7b72>
ⓘ Interface with external programs via stdin/stdout.
Tff7b72</div>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>CommandTff7b72</FormLabel>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceCommandTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6fftextTa5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ffe.g. netcat -l 5757Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Respawn Delay (s)Tff7b72</FormLabel>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceRespawnDelayTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6ffnumberTa5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ff5Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>
T8b949e<!-- Advanced Sections -->
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffspace-y-4 pt-4Ta5d6ff"Tff7b72>
T8b949e<!-- RNode Advanced Tools -->
Tff7b72<ExpandingSection
Te6edf3v-if=Ta5d6ff"Ta5d6ff[Ta5d6ff'Ta5d6ffRNodeInterfaceTa5d6ff'Ta5d6ff, Ta5d6ff'Ta5d6ffRNodeIPInterfaceTa5d6ff'Ta5d6ff].includes(newInterfaceType)Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffglass-card !p-0 overflow-hiddenTa5d6ff"
Tff7b72>
Tff7b72<template #title
Tff7b72>Tff7b72<span Te6edf3class=Ta5d6ff"Ta5d6fftext-sm font-boldTa5d6ff"Tff7b72>Calculated ParametersTff7b72</span>Tff7b72</template
>
Tff7b72<template #contentTff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffp-6 space-y-6Ta5d6ff"Tff7b72>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Antenna Gain (dBi)Tff7b72</FormLabel>
Tff7b72<input
v-model.Te6edf3number=Ta5d6ff"Ta5d6ffRNodeInterfaceLoRaParameters.antennaGainTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6ffnumberTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffgrid grid-cols-3 gap-3Ta5d6ff"Tff7b72>
Tff7b72<div
Te6edf3class=Ta5d6ff"Ta5d6ffbg-blue-500/5 p-3 rounded-2xl border border-blue-500/10 text-centerTa5d6ff"
Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6fftext-[10px] uppercase font-bold text-blue-500 mb-1Ta5d6ff"Tff7b72>
Sensitivity
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6fftext-lg font-mono font-boldTa5d6ff"Tff7b72>
{{ RNodeInterfaceLoRaParameters.sensitivity ?? "???" }}
Tff7b72</div>
Tff7b72</div>
Tff7b72<div
Te6edf3class=Ta5d6ff"Ta5d6ffbg-blue-500/5 p-3 rounded-2xl border border-blue-500/10 text-centerTa5d6ff"
Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6fftext-[10px] uppercase font-bold text-blue-500 mb-1Ta5d6ff"Tff7b72>
Data Rate
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6fftext-lg font-mono font-boldTa5d6ff"Tff7b72>
{{ RNodeInterfaceLoRaParameters.dataRate ?? "???" }}
Tff7b72</div>
Tff7b72</div>
Tff7b72<div
Te6edf3class=Ta5d6ff"Ta5d6ffbg-blue-500/5 p-3 rounded-2xl border border-blue-500/10 text-centerTa5d6ff"
Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6fftext-[10px] uppercase font-bold text-blue-500 mb-1Ta5d6ff"Tff7b72>
Link Budget
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6fftext-lg font-mono font-boldTa5d6ff"Tff7b72>
{{ RNodeInterfaceLoRaParameters.linkBudget ?? "???" }}
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>
Tff7b72</template>
Tff7b72</ExpandingSection>
T8b949e<!-- Interface Discovery Settings -->
Tff7b72<ExpandingSection Te6edf3class=Ta5d6ff"Ta5d6ffglass-card !p-0 overflow-hiddenTa5d6ff"Tff7b72>
Tff7b72<template #title
Tff7b72>Tff7b72<span Te6edf3class=Ta5d6ff"Ta5d6fftext-sm font-boldTa5d6ff"Tff7b72>Interface DiscoveryTff7b72</span>Tff7b72</template
>
Tff7b72<template #contentTff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffp-6 space-y-6Ta5d6ff"Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex items-center justify-betweenTa5d6ff"Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffmax-w-mdTa5d6ff"Tff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-label !mb-0Ta5d6ff"
Tff7b72>Publish Discovery AnnounceTff7b72</FormLabel
>
Tff7b72<p Te6edf3class=Ta5d6ff"Ta5d6fftext-xs text-gray-400Ta5d6ff"Tff7b72>
Makes your node visible to others on the network.
Tff7b72</p>
Tff7b72</div>
Tff7b72<Toggle Te6edf3v-model=Ta5d6ff"Ta5d6ffdiscovery.discoverableTa5d6ff" Tff7b72/>
Tff7b72</div>
Tff7b72<div
Te6edf3v-if=Ta5d6ff"Ta5d6ffdiscovery.discoverableTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffspace-y-4 pt-4 border-t border-gray-100 dark:border-zinc-800 animate-in fade-in slide-in-from-top-2Ta5d6ff"
Tff7b72>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Discovery NameTff7b72</FormLabel>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffdiscovery.discovery_nameTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6fftextTa5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ffHuman-friendly nameTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffgrid grid-cols-2 gap-4Ta5d6ff"Tff7b72>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Announce Interval (m)Tff7b72</FormLabel>
Tff7b72<input
v-model.Te6edf3number=Ta5d6ff"Ta5d6ffdiscovery.announce_intervalTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6ffnumberTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Reachable OnTff7b72</FormLabel>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffdiscovery.reachable_onTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6fftextTa5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ffIP or HostnameTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffgrid grid-cols-1 md:grid-cols-3 gap-4Ta5d6ff"Tff7b72>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Latitude (optional)Tff7b72</FormLabel>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffdiscovery.latitudeTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6fftextTa5d6ff"
Te6edf3inputmode=Ta5d6ff"Ta5d6ffdecimalTa5d6ff"
Te6edf3autocomplete=Ta5d6ff"Ta5d6ffoffTa5d6ff"
Te6edf3aria-required=Ta5d6ff"Ta5d6fffalseTa5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ffLeave blank if unknownTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Longitude (optional)Tff7b72</FormLabel>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffdiscovery.longitudeTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6fftextTa5d6ff"
Te6edf3inputmode=Ta5d6ff"Ta5d6ffdecimalTa5d6ff"
Te6edf3autocomplete=Ta5d6ff"Ta5d6ffoffTa5d6ff"
Te6edf3aria-required=Ta5d6ff"Ta5d6fffalseTa5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ffLeave blank if unknownTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"
Tff7b72>Height in metres (optional)Tff7b72</FormLabel
>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffdiscovery.heightTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6fftextTa5d6ff"
Te6edf3inputmode=Ta5d6ff"Ta5d6ffdecimalTa5d6ff"
Te6edf3autocomplete=Ta5d6ff"Ta5d6ffoffTa5d6ff"
Te6edf3aria-required=Ta5d6ff"Ta5d6fffalseTa5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ffLeave blank if unknownTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffgrid grid-cols-2 gap-4Ta5d6ff"Tff7b72>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Discovery stamp valueTff7b72</FormLabel>
Tff7b72<input
v-model.Te6edf3number=Ta5d6ff"Ta5d6ffdiscovery.discovery_stamp_valueTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6ffnumberTa5d6ff"
Te6edf3min=Ta5d6ff"Ta5d6ff1Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex flex-wrap items-center justify-between gap-4Ta5d6ff"Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex items-center justify-between gap-4 max-w-mdTa5d6ff"Tff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-label !mb-0Ta5d6ff"
Tff7b72>Encrypt discoveryTff7b72</FormLabel
>
Tff7b72<Toggle Te6edf3v-model=Ta5d6ff"Ta5d6ffdiscovery.discovery_encryptTa5d6ff" Tff7b72/>
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex items-center justify-between gap-4 max-w-mdTa5d6ff"Tff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-label !mb-0Ta5d6ff"
Tff7b72>Publish IFAC in announceTff7b72</FormLabel
>
Tff7b72<Toggle Te6edf3v-model=Ta5d6ff"Ta5d6ffdiscovery.publish_ifacTa5d6ff" Tff7b72/>
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>
Tff7b72</template>
Tff7b72</ExpandingSection>
T8b949e<!-- Global Discovery Settings -->
Tff7b72<ExpandingSection Te6edf3class=Ta5d6ff"Ta5d6ffglass-card !p-0 overflow-hiddenTa5d6ff"Tff7b72>
Tff7b72<template #title
Tff7b72>Tff7b72<span Te6edf3class=Ta5d6ff"Ta5d6fftext-sm font-boldTa5d6ff"Tff7b72>Discovery Listener (Peer)Tff7b72</span>Tff7b72</template
>
Tff7b72<template #contentTff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffp-6 space-y-6Ta5d6ff"Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex items-center justify-betweenTa5d6ff"Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffmax-w-mdTa5d6ff"Tff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-label !mb-0Ta5d6ff"
Tff7b72>Enable Discovery ListenerTff7b72</FormLabel
>
Tff7b72<p Te6edf3class=Ta5d6ff"Ta5d6fftext-xs text-gray-400Ta5d6ff"Tff7b72>
Listen for announced interfaces and optionally auto-connect.
Tff7b72</p>
Tff7b72</div>
Tff7b72<Toggle Te6edf3v-model=Ta5d6ff"Ta5d6ffreticulumDiscovery.discover_interfacesTa5d6ff" Tff7b72/>
Tff7b72</div>
Tff7b72<div
Te6edf3v-if=Ta5d6ff"Ta5d6ffreticulumDiscovery.discover_interfacesTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffspace-y-4 pt-4 border-t border-gray-100 dark:border-zinc-800 animate-in fade-in slide-in-from-top-2Ta5d6ff"
Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffgrid grid-cols-1 md:grid-cols-2 gap-4Ta5d6ff"Tff7b72>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffreticulumDiscovery.interface_discovery_whitelistTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6fftextTa5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ffWhitelist (names, hosts, IDs)Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffreticulumDiscovery.interface_discovery_blacklistTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6fftextTa5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ffBlacklist (names, hosts, IDs)Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex justify-endTa5d6ff"Tff7b72>
Tff7b72<button
Te6edf3type=Ta5d6ff"Ta5d6ffbuttonTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffprimary-chip !text-[10px]Ta5d6ff"
Te6edf3:disabled=Ta5d6ff"Ta5d6ffsavingDiscoveryTa5d6ff"
@Te6edf3click=Ta5d6ff"Ta5d6ffsaveReticulumDiscoveryConfigTa5d6ff"
Tff7b72>
Tff7b72<MaterialDesignIcon Te6edf3icon-name=Ta5d6ff"Ta5d6ffcontent-saveTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffsize-3Ta5d6ff" Tff7b72/>
Save Listener Prefs
Tff7b72</button>
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>
Tff7b72</template>
Tff7b72</ExpandingSection>
T8b949e<!-- Shared Advanced Settings -->
Tff7b72<ExpandingSection Te6edf3class=Ta5d6ff"Ta5d6ffglass-card !p-0 overflow-hiddenTa5d6ff"Tff7b72>
Tff7b72<template #title
Tff7b72>Tff7b72<span Te6edf3class=Ta5d6ff"Ta5d6fftext-sm font-boldTa5d6ff"
Tff7b72>Advanced Parameters (IFAC, Mode)Tff7b72</span
>Tff7b72</template
>
Tff7b72<template #contentTff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffp-6 space-y-6Ta5d6ff"Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffgrid grid-cols-2 gap-4Ta5d6ff"Tff7b72>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Interface ModeTff7b72</FormLabel>
Tff7b72<select Te6edf3v-model=Ta5d6ff"Ta5d6ffsharedInterfaceSettings.modeTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"Tff7b72>
Tff7b72<option Te6edf3:value=Ta5d6ff"Ta5d6ffundefinedTa5d6ff"Tff7b72>Default (Full)Tff7b72</option>
Tff7b72<option Te6edf3value=Ta5d6ff"Ta5d6fffullTa5d6ff"Tff7b72>FullTff7b72</option>
Tff7b72<option Te6edf3value=Ta5d6ff"Ta5d6ffgatewayTa5d6ff"Tff7b72>GatewayTff7b72</option>
Tff7b72<option Te6edf3value=Ta5d6ff"Ta5d6ffaccess_pointTa5d6ff"Tff7b72>Access PointTff7b72</option>
Tff7b72<option Te6edf3value=Ta5d6ff"Ta5d6ffroamingTa5d6ff"Tff7b72>RoamingTff7b72</option>
Tff7b72<option Te6edf3value=Ta5d6ff"Ta5d6ffboundaryTa5d6ff"Tff7b72>BoundaryTff7b72</option>
Tff7b72</select>
Tff7b72</div>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Forced BitrateTff7b72</FormLabel>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffsharedInterfaceSettings.bitrateTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6ffnumberTa5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ffbpsTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffspace-y-4 pt-4 border-t border-gray-100 dark:border-zinc-800Ta5d6ff"Tff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Interface Access Code (IFAC)Tff7b72</FormLabel>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffgrid grid-cols-1 md:grid-cols-2 gap-4Ta5d6ff"Tff7b72>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffsharedInterfaceSettings.network_nameTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6fftextTa5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ffNetwork NameTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffsharedInterfaceSettings.passphraseTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6fftextTa5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ffPassphraseTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>
Tff7b72</template>
Tff7b72</ExpandingSection>
Tff7b72</div>
T8b949e<!-- Footer Save Action -->
Tff7b72<div
Te6edf3class=Ta5d6ff"Ta5d6ffpt-8 flex items-center justify-between gap-4 border-t border-gray-200 dark:border-zinc-800Ta5d6ff"
Tff7b72>
Tff7b72<button
Te6edf3type=Ta5d6ff"Ta5d6ffbuttonTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffsecondary-chip !px-10 !py-3 !text-smTa5d6ff"
@Te6edf3click=Ta5d6ff"Te6edf3$router.pushTa5d6ff({ name: Ta5d6ff'Ta5d6ffinterfacesTa5d6ff'Ta5d6ff })Ta5d6ff"
Tff7b72>
Cancel
Tff7b72</button>
Tff7b72<button
Te6edf3type=Ta5d6ff"Ta5d6ffbuttonTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffprimary-chip !px-16 !py-3 !text-smTa5d6ff"
Te6edf3:disabled=Ta5d6ff"Ta5d6ffisSavingTa5d6ff"
@Te6edf3click=Ta5d6ff"Ta5d6ffsaveInterfaceTa5d6ff"
Tff7b72>
Tff7b72<MaterialDesignIcon
Te6edf3:icon-name=Ta5d6ff"Ta5d6ffisSaving ? Ta5d6ff'Ta5d6ffloadingTa5d6ff'Ta5d6ff : isEditingInterface ? Ta5d6ff'Ta5d6ffcontent-saveTa5d6ff'Ta5d6ff : Ta5d6ff'Ta5d6ffplusTa5d6ff'Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffsize-5Ta5d6ff"
Te6edf3:class=Ta5d6ff"Ta5d6ff{ Ta5d6ff'Ta5d6ffanimate-spinTa5d6ff'Ta5d6ff: isSaving }Ta5d6ff"
Tff7b72/>
{{ isEditingInterface ? "Update Connection" : "Create Connection" }}
Tff7b72</button>
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>
Tff7b72<aside
Te6edf3class=Ta5d6ff"Ta5d6ffw-full shrink-0 space-y-4 xl:w-96 xl:sticky xl:top-4 xl:self-start xl:max-h-[min(calc(100dvh-6rem),920px)] xl:overflow-y-auto xl:border-l border-gray-200/80 dark:border-zinc-800 xl:pl-8Ta5d6ff"
Te6edf3:aria-label=Ta5d6ff"Te6edf3$tTa5d6ff(Ta5d6ff'Ta5d6ffinterfaces.add_interface_sidebar_a11yTa5d6ff'Ta5d6ff)Ta5d6ff"
Tff7b72>
Tff7b72<div
Te6edf3v-if=Ta5d6ff"Ta5d6ff!isEditingInterface && communityPresetsEnabled && communityInterfaces.length > 0Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffglass-card !p-0 overflow-hiddenTa5d6ff"
Tff7b72>
Tff7b72<div
Te6edf3class=Ta5d6ff"Ta5d6ffbg-gray-50/50 dark:bg-zinc-800/50 p-4 border-b border-gray-200 dark:border-zinc-800 flex items-center justify-between gap-2Ta5d6ff"
Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffmin-w-0Ta5d6ff"Tff7b72>
Tff7b72<h2 Te6edf3class=Ta5d6ff"Ta5d6fffont-bold text-gray-900 dark:text-white flex items-center gap-2 text-smTa5d6ff"Tff7b72>
Tff7b72<MaterialDesignIcon Te6edf3icon-name=Ta5d6ff"Ta5d6fflightning-boltTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffsize-5 text-yellow-500Ta5d6ff" Tff7b72/>
{{ Te6edf3$t("interfaces.community_quick_start") }}
Tff7b72</h2>
Tff7b72<p Te6edf3class=Ta5d6ff"Ta5d6fftext-xs text-gray-500 dark:text-zinc-400 mt-0.5Ta5d6ff"Tff7b72>
{{ Te6edf3$t("interfaces.community_quick_start_hint") }}
Tff7b72</p>
Tff7b72</div>
Tff7b72<button
Te6edf3type=Ta5d6ff"Ta5d6ffbuttonTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6fftext-gray-400 hover:text-gray-600 dark:hover:text-zinc-200 transition-colors p-1 shrink-0Ta5d6ff"
Te6edf3:title=Ta5d6ff"Te6edf3$tTa5d6ff(Ta5d6ff'Ta5d6ffinterfaces.community_quick_start_hideTa5d6ff'Ta5d6ff)Ta5d6ff"
@Te6edf3click=Ta5d6ff"Ta5d6ffupdateConfig({ show_suggested_community_interfaces: false })Ta5d6ff"
Tff7b72>
Tff7b72<MaterialDesignIcon Te6edf3icon-name=Ta5d6ff"Ta5d6ffcloseTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffsize-5Ta5d6ff" Tff7b72/>
Tff7b72</button>
Tff7b72</div>
Tff7b72<div
Te6edf3class=Ta5d6ff"Ta5d6ffdivide-y divide-gray-100 dark:divide-zinc-800 max-h-[min(50vh,28rem)] overflow-y-autoTa5d6ff"
Tff7b72>
Tff7b72<div
Te6edf3v-for=Ta5d6ff"Ta5d6ffcommunityIface in communityInterfacesTa5d6ff"
Te6edf3:key=Ta5d6ff"
Ta5d6ff communityIface.name +
(communityIface.target_host || Ta5d6ff'Ta5d6ff'Ta5d6ff) +
(communityIface.target_port || Ta5d6ff'Ta5d6ff'Ta5d6ff)
Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffflex p-3 sm:p-4 items-center gap-2 hover:bg-gray-50/30 dark:hover:bg-zinc-800/20 transition-colorsTa5d6ff"
Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffmin-w-0 flex-1Ta5d6ff"Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6fffont-bold text-sm text-gray-900 dark:text-zinc-100Ta5d6ff"Tff7b72>
{{ communityIface.name }}
Tff7b72</div>
Tff7b72<div
Te6edf3class=Ta5d6ff"Ta5d6fftext-[10px] font-mono text-gray-500 dark:text-zinc-400 mt-0.5 flex flex-wrap items-center gap-2Ta5d6ff"
Tff7b72>
Tff7b72<MaterialDesignIcon Te6edf3icon-name=Ta5d6ff"Ta5d6ffserver-networkTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffsize-3 shrink-0Ta5d6ff" Tff7b72/>
Tff7b72<template Te6edf3v-if=Ta5d6ff"Ta5d6ffcommunityIface.type === Ta5d6ff'Ta5d6ffI2PInterfaceTa5d6ff'Ta5d6ff"Tff7b72>
{{ communityIface.target_host }}
Tff7b72</template>
Tff7b72<template v-elseTff7b72>
{{ communityIface.target_host }}:{{ communityIface.target_port }}
Tff7b72</template>
Tff7b72<span
Te6edf3v-if=Ta5d6ff"Ta5d6ffcommunityIface.online === trueTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6fftext-green-500 flex items-center gap-1Ta5d6ff"
Tff7b72>
Tff7b72<span Te6edf3class=Ta5d6ff"Ta5d6ffsize-1.5 rounded-full bg-green-500 animate-pulseTa5d6ff"Tff7b72>Tff7b72</span>
Online
Tff7b72</span>
Tff7b72<span Te6edf3v-else-if=Ta5d6ff"Ta5d6ffcommunityIface.online === falseTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6fftext-red-500Ta5d6ff"
Tff7b72>OfflineTff7b72</span
>
Tff7b72</div>
Tff7b72<div
Te6edf3v-if=Ta5d6ff"Ta5d6ffcommunityIface.descriptionTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6fftext-[10px] text-gray-400 dark:text-zinc-500 mt-1 italic line-clamp-2Ta5d6ff"
Tff7b72>
{{ communityIface.description }}
Tff7b72</div>
Tff7b72</div>
Tff7b72<button
Te6edf3type=Ta5d6ff"Ta5d6ffbuttonTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffprimary-chip !py-1.5 !px-2 !text-[10px] shrink-0Ta5d6ff"
@Te6edf3click=Ta5d6ff"Ta5d6ffquickAddInterfaceFromConfig(communityIface)Ta5d6ff"
Tff7b72>
{{ Te6edf3$t("interfaces.community_use_preset") }}
Tff7b72</button>
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>
Tff7b72<div
Te6edf3v-else-if=Ta5d6ff"
Ta5d6ff !isEditingInterface && communityPresetsDismissed && communityInterfaces.length > 0
Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffglass-card p-4 space-y-3Ta5d6ff"
Tff7b72>
Tff7b72<p Te6edf3class=Ta5d6ff"Ta5d6fftext-sm text-gray-600 dark:text-zinc-400Ta5d6ff"Tff7b72>
{{ Te6edf3$t("interfaces.community_presets_hidden_hint") }}
Tff7b72</p>
Tff7b72<button
Te6edf3type=Ta5d6ff"Ta5d6ffbuttonTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffprimary-chip !py-2 !px-4 !text-xs w-fullTa5d6ff"
@Te6edf3click=Ta5d6ff"Ta5d6ffupdateConfig({ show_suggested_community_interfaces: true })Ta5d6ff"
Tff7b72>
{{ Te6edf3$t("interfaces.community_presets_show_again") }}
Tff7b72</button>
Tff7b72</div>
Tff7b72<div
Te6edf3v-else-if=Ta5d6ff"
Ta5d6ff !isEditingInterface &&
communityPresetsEnabled &&
communityInterfacesFetchDone &&
communityInterfaces.length === 0
Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffglass-card p-4 text-sm text-gray-500 dark:text-zinc-400Ta5d6ff"
Tff7b72>
{{ Te6edf3$t("interfaces.community_presets_empty") }}
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffgrid grid-cols-1 gap-4Ta5d6ff"Tff7b72>
Tff7b72<div
Te6edf3class=Ta5d6ff"Ta5d6ffglass-card flex items-center gap-4 bg-blue-50/30 dark:bg-blue-900/10 border-blue-100 dark:border-blue-900/30Ta5d6ff"
Tff7b72>
Tff7b72<div
Te6edf3class=Ta5d6ff"Ta5d6ffsize-10 rounded-2xl bg-blue-500/10 flex items-center justify-center text-blue-500 shrink-0Ta5d6ff"
Tff7b72>
Tff7b72<MaterialDesignIcon Te6edf3icon-name=Ta5d6ff"Ta5d6ffmap-search-outlineTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffsize-6Ta5d6ff" Tff7b72/>
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex-1 min-w-0Ta5d6ff"Tff7b72>
Tff7b72<h3 Te6edf3class=Ta5d6ff"Ta5d6fftext-sm font-bold text-gray-900 dark:text-whiteTa5d6ff"Tff7b72>
{{ Te6edf3$t("interfaces.find_more_nodes") }}
Tff7b72</h3>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex flex-wrap gap-2 mt-1Ta5d6ff"Tff7b72>
Tff7b72<a
Te6edf3href=Ta5d6ff"Ta5d6ffhttps://directory.rns.recipes/Ta5d6ff"
Te6edf3target=Ta5d6ff"Ta5d6ff_blankTa5d6ff"
Te6edf3rel=Ta5d6ff"Ta5d6ffnoopener noreferrerTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffsecondary-chip !py-1 !px-2 !text-[9px]Ta5d6ff"
Tff7b72>rns.recipesTff7b72</a
>
Tff7b72<a
Te6edf3href=Ta5d6ff"Ta5d6ffhttps://rmap.world/Ta5d6ff"
Te6edf3target=Ta5d6ff"Ta5d6ff_blankTa5d6ff"
Te6edf3rel=Ta5d6ff"Ta5d6ffnoopener noreferrerTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffsecondary-chip !py-1 !px-2 !text-[9px]Ta5d6ff"
Tff7b72>rmap.worldTff7b72</a
>
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>
Tff7b72<div
Te6edf3class=Ta5d6ff"Ta5d6ffglass-card flex flex-col gap-2 !p-4 bg-emerald-50/20 dark:bg-emerald-900/5 border-emerald-100 dark:border-emerald-900/20Ta5d6ff"
Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex items-center justify-between gap-2Ta5d6ff"Tff7b72>
Tff7b72<h3
Te6edf3class=Ta5d6ff"Ta5d6fftext-xs font-bold text-emerald-600 dark:text-emerald-500 uppercase tracking-widest flex items-center gap-2Ta5d6ff"
Tff7b72>
Tff7b72<MaterialDesignIcon Te6edf3icon-name=Ta5d6ff"Ta5d6ffimportTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffsize-4Ta5d6ff" Tff7b72/>
{{ Te6edf3$t("interfaces.quick_import") }}
Tff7b72</h3>
Tff7b72<span Te6edf3class=Ta5d6ff"Ta5d6fftext-[10px] text-gray-400 shrink-0Ta5d6ff"Tff7b72>{{
Te6edf3$t("interfaces.quick_import_paste_hint")
}}Tff7b72</span>
Tff7b72</div>
Tff7b72<textarea
Te6edf3v-model=Ta5d6ff"Ta5d6ffrawConfigInputTa5d6ff"
Te6edf3:placeholder=Ta5d6ff"Te6edf3$tTa5d6ff(Ta5d6ff'Ta5d6ffinterfaces.quick_import_placeholderTa5d6ff'Ta5d6ff)Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffw-full h-20 bg-white/50 dark:bg-zinc-900/50 border border-emerald-100/50 dark:border-emerald-900/30 rounded-xl p-2 text-[10px] font-mono focus:ring-1 focus:ring-emerald-500 outline-none transitionTa5d6ff"
@Te6edf3input=Ta5d6ff"Ta5d6ffhandleRawConfigInputTa5d6ff"
Tff7b72>Tff7b72</textarea>
Tff7b72<div Te6edf3v-if=Ta5d6ff"Ta5d6ffdetectedConfigs.length > 1Ta5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffflex flex-wrap gap-2 mt-1Ta5d6ff"Tff7b72>
Tff7b72<button
Te6edf3v-for=Ta5d6ff"Ta5d6ffcfg in detectedConfigsTa5d6ff"
Te6edf3:key=Ta5d6ff"Ta5d6ffcfg.nameTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6ffbuttonTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffbg-emerald-500/10 hover:bg-emerald-500/20 border border-emerald-500/20 rounded-lg px-2 py-1 text-[9px] font-bold text-emerald-600 dark:text-emerald-400 transitionTa5d6ff"
@Te6edf3click=Ta5d6ff"Ta5d6ffquickAddInterfaceFromConfig(cfg)Ta5d6ff"
Tff7b72>
{{ Te6edf3$t("interfaces.quick_import_apply", { name: cfg.name }) }}
Tff7b72</button>
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>
Tff7b72</aside>
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>
Tff7b72</template>
Tff7b72<scriptTff7b72>
import DialogUtils from "../../js/DialogUtils";
import ToastUtils from "../../js/ToastUtils";
import { numOrNull } from "../../js/interfaceDiscoveryUtils";
import ExpandingSection from "./ExpandingSection.vue";
import FormLabel from "../forms/FormLabel.vue";
import Toggle from "../forms/Toggle.vue";
import GlobalState from "../../js/GlobalState";
import MaterialDesignIcon from "../MaterialDesignIcon.vue";
export default {
name: "AddInterfacePage",
components: {
MaterialDesignIcon,
FormLabel,
ExpandingSection,
Toggle,
},
data() {
return {
rawConfigInput: "",
detectedConfigs: [],
isSaving: false,
isEditingInterface: false,
config: null,
communityInterfaces: [],
communityInterfacesFetchDone: false,
comports: [],
newInterfaceName: null,
newInterfaceType: null,
newInterfaceGroupID: null,
newInterfaceMulticastAddressType: null,
newInterfaceDevices: null,
newInterfaceIgnoredDevices: null,
newInterfaceDiscoveryScope: null,
newInterfaceDiscoveryPort: null,
newInterfaceDataPort: null,
newInterfaceTargetHost: null,
newInterfaceTargetPort: null,
newInterfaceTransportIdentity: null,
newInterfaceListenIp: null,
newInterfaceListenPort: null,
newInterfaceNetworkDevice: null,
newInterfacePreferIPV6: false,
newInterfaceKISSFramingEnabled: false,
newInterfaceI2PTunnelingEnabled: false,
newInterfaceConnectTimeout: null,
newInterfaceMaxReconnectTries: null,
newInterfaceFixedMTU: null,
newInterfaceConfiguredBitrate: null,
newInterfaceConnectable: true,
newInterfaceBackboneListenMode: false,
newInterfaceBackboneListenPort: null,
newInterfaceBackboneListenIp: null,
newInterfaceBackboneListenDevice: null,
sharedInterfaceSettings: {
mode: null,
network_name: null,
passphrase: null,
ifac_size: null,
bitrate: null,
},
discovery: {
discoverable: false,
discovery_name: "",
announce_interval: 360,
reachable_on: "",
discovery_stamp_value: 14,
discovery_encrypt: false,
publish_ifac: false,
latitude: null,
longitude: null,
height: null,
discovery_frequency: null,
discovery_bandwidth: null,
discovery_modulation: null,
},
reticulumDiscovery: {
discover_interfaces: false,
interface_discovery_sources: "",
interface_discovery_whitelist: "",
interface_discovery_blacklist: "",
required_discovery_value: null,
autoconnect_discovered_interfaces: 0,
network_identity: "",
},
savingDiscovery: false,
newInterfaceForwardIp: null,
newInterfaceForwardPort: null,
I2PSettings: {
newInterfacePeers: [],
},
RNodeMultiInterface: {
port: null,
subInterfaces: [],
},
newInterfacePort: null,
newInterfaceRNodeUseIP: false,
newInterfaceRNodeIPHost: "localhost",
newInterfaceRNodeIPPort: "7633",
RNodeGHzValue: 0,
RNodeMHzValue: 0,
RNodekHzValue: 0,
newInterfaceFrequency: null,
newInterfaceBandwidth: 125000,
newInterfaceTxpower: 7,
newInterfaceSpreadingFactor: 12,
newInterfaceCodingRate: 5,
// Serial, KISS, and AX25KISS options
newInterfaceSpeed: null,
newInterfaceDatabits: null,
newInterfaceParity: null,
newInterfaceStopbits: null,
// KISS and AX25KISS
newInterfacePreamble: null,
newInterfaceTXTail: null,
newInterfacePersistence: null,
newInterfaceSlotTime: null,
// AX25 KISS only
newInterfaceSSID: null,
// RNode and KISS
newInterfaceCallsign: null,
newInterfaceIDCallsign: null,
newInterfaceIDInterval: null,
newInterfaceFlowControl: false,
newInterfaceAirtimeLimitLong: null,
newInterfaceAirtimeLimitShort: null,
// Pipe interface
newInterfaceCommand: null,
newInterfaceRespawnDelay: null,
RNodeInterfaceDefaults: {
// bandwidth in hz
bandwidths: [
7800, // 7.8 kHz
10400, // 10.4 kHz
15600, // 15.6 kHz
20800, // 20.8 kHz
31250, // 31.25 kHz
41700, // 41.7 kHz
62500, // 62.5 kHz
125000, // 125 kHz
250000, // 250 kHz
500000, // 500 kHz
1625000, // 1625 kHz (for 2.4 GHz SX1280)
],
codingrates: [
5, // 4:5
6, // 4:6
7, // 4:7
8, // 4:8
],
spreadingfactors: [5, 6, 7, 8, 9, 10, 11, 12],
},
RNodeInterfaceLoRaParameters: {
antennaGain: 0,
noiseFloor: 5,
sensitivity: null,
dataRate: null,
linkBudget: null,
},
};
},
computed: {
communityPresetsEnabled() {
if (this.isEditingInterface) {
return false;
}
const c = this.config;
if (!c) {
return true;
}
const v = c.show_suggested_community_interfaces;
if (v === undefined || v === null) {
return true;
}
return this.parseBool(v);
},
communityPresetsDismissed() {
if (this.isEditingInterface || !this.config) {
return false;
}
const v = this.config.show_suggested_community_interfaces;
if (v === undefined || v === null) {
return false;
}
return !this.parseBool(v);
},
formattedFrequency() {
const totalHz = this.calculateFrequencyInHz();
if (totalHz >= 1e9) {
return `${(totalHz / 1e9).toFixed(3)} GHz`;
} else if (totalHz >= 1e6) {
return `${(totalHz / 1e6).toFixed(3)} MHz`;
} else if (totalHz >= 1e3) {
return `${(totalHz / 1e3).toFixed(3)} kHz`;
}
return `${totalHz} Hz`;
},
},
watch: {
newInterfaceBandwidth: "updateRNodeCalculations",
newInterfaceSpreadingFactor: "updateRNodeCalculations",
newInterfaceCodingRate: "updateRNodeCalculations",
newInterfaceTxpower: "updateRNodeCalculations",
"RNodeInterfaceLoRaParameters.antennaGain": "updateRNodeCalculations",
},
mounted() {
this.getConfig();
this.loadReticulumDiscoveryConfig();
this.loadComports();
this.loadCommunityInterfaces();
// check if we are editing an interface
const interfaceName = this.$route.query.interface_name;
if (interfaceName != null) {
this.isEditingInterface = true;
this.loadInterfaceToEdit(interfaceName);
}
// check if we have a discovered interface prefill payload
if (this.$route.query.from_discovered) {
this.applyDiscoveredInterfacePrefill();
}
},
methods: {
async getConfig() {
try {
const response = await window.api.get(`/api/v1/config`);
this.config = response.data.config;
} catch (e) {
console.log(e);
}
},
async updateConfig(config) {
try {
const response = await window.api.patch("/api/v1/config", config);
this.config = response.data.config;
} catch (e) {
ToastUtils.error(this.$t("common.save_failed"));
console.log(e);
}
},
parseBool(value) {
if (typeof value === "string") {
return ["true", "yes", "1", "y", "on"].includes(value.toLowerCase());
}
return Boolean(value);
},
numOrNull,
async loadReticulumDiscoveryConfig() {
try {
const response = await window.api.get(`/api/v1/reticulum/discovery`);
const discovery = response.data?.discovery ?? {};
this.reticulumDiscovery.discover_interfaces = this.parseBool(discovery.discover_interfaces);
this.reticulumDiscovery.interface_discovery_whitelist = discovery.interface_discovery_whitelist ?? "";
this.reticulumDiscovery.interface_discovery_blacklist = discovery.interface_discovery_blacklist ?? "";
} catch (e) {
console.log(e);
}
},
async saveReticulumDiscoveryConfig() {
if (this.savingDiscovery) return;
this.savingDiscovery = true;
try {
const payload = {
discover_interfaces: this.reticulumDiscovery.discover_interfaces,
interface_discovery_whitelist: this.reticulumDiscovery.interface_discovery_whitelist || null,
interface_discovery_blacklist: this.reticulumDiscovery.interface_discovery_blacklist || null,
};
await window.api.patch(`/api/v1/reticulum/discovery`, payload);
ToastUtils.success("Discovery listener preferences saved.");
} catch (e) {
ToastUtils.error("Failed to save discovery preferences.");
console.log(e);
} finally {
this.savingDiscovery = false;
}
},
async loadComports() {
try {
const response = await window.api.get(`/api/v1/comports`);
this.comports = response.data.comports;
} catch (e) {
console.log(e);
}
},
async loadCommunityInterfaces() {
try {
const response = await window.api.get(`/api/v1/community-interfaces`);
this.communityInterfaces = response.data.interfaces ?? [];
} catch (e) {
console.log(e);
this.communityInterfaces = [];
} finally {
this.communityInterfacesFetchDone = true;
}
},
async loadInterfaceToEdit(interfaceName) {
try {
const response = await window.api.get(`/api/v1/reticulum/interfaces`);
const interfaces = response.data.interfaces;
const iface = interfaces[interfaceName];
if (!iface) {
DialogUtils.alert(this.$t("interfaces.interface_not_found"));
this.$router.push({ name: "interfaces" });
return;
}
this.newInterfaceName = interfaceName;
this.newInterfaceType = iface.type;
this.newInterfaceTargetHost = iface.target_host ?? iface.remote ?? null;
this.newInterfaceTargetPort = iface.target_port ?? null;
this.newInterfaceTransportIdentity = iface.transport_identity ?? null;
if (iface.type === "I2PInterface" && Array.isArray(iface.peers)) {
this.I2PSettings.newInterfacePeers = [...iface.peers];
}
this.newInterfaceListenIp = iface.listen_ip;
this.newInterfaceListenPort = iface.listen_port;
this.newInterfaceForwardIp = iface.forward_ip;
this.newInterfaceForwardPort = iface.forward_port;
this.newInterfaceNetworkDevice = iface.device;
this.newInterfacePreferIPV6 = this.parseBool(iface.prefer_ipv6);
this.newInterfaceKISSFramingEnabled = this.parseBool(iface.kiss_framing);
this.newInterfaceI2PTunnelingEnabled = this.parseBool(iface.i2p_tunneled);
this.newInterfaceConnectTimeout = iface.connect_timeout ?? null;
this.newInterfaceMaxReconnectTries = iface.max_reconnect_tries ?? null;
this.newInterfaceFixedMTU = iface.fixed_mtu ?? null;
this.newInterfaceConnectable =
iface.connectable === undefined ? true : this.parseBool(iface.connectable);
if (iface.type === "BackboneInterface") {
this.newInterfaceBackboneListenMode = iface.listen_port != null && iface.listen_port !== "";
this.newInterfaceBackboneListenIp = iface.listen_ip ?? null;
this.newInterfaceBackboneListenPort = iface.listen_port ?? null;
this.newInterfaceBackboneListenDevice = iface.device ?? null;
}
this.newInterfaceGroupID = iface.group_id ?? null;
this.newInterfaceMulticastAddressType = iface.multicast_address_type ?? null;
this.newInterfaceDevices = iface.devices ?? null;
this.newInterfaceIgnoredDevices = iface.ignored_devices ?? null;
this.newInterfaceDiscoveryScope = iface.discovery_scope ?? null;
this.newInterfaceDiscoveryPort = iface.discovery_port ?? null;
this.newInterfaceDataPort = iface.data_port ?? null;
this.newInterfaceConfiguredBitrate = iface.configured_bitrate ?? null;
this.newInterfaceFlowControl = this.parseBool(iface.flow_control);
this.newInterfaceCallsign = iface.callsign ?? null;
this.newInterfaceIDCallsign = iface.id_callsign ?? null;
this.newInterfaceIDInterval = iface.id_interval ?? null;
this.newInterfaceAirtimeLimitLong = iface.airtime_limit_long ?? null;
this.newInterfaceAirtimeLimitShort = iface.airtime_limit_short ?? null;
this.newInterfaceSSID = iface.ssid ?? null;
this.newInterfaceSpeed = iface.speed ?? null;
this.newInterfaceDatabits = iface.databits ?? null;
this.newInterfaceParity = iface.parity ?? null;
this.newInterfaceStopbits = iface.stopbits ?? null;
this.newInterfacePreamble = iface.preamble ?? null;
this.newInterfaceTXTail = iface.txtail ?? null;
this.newInterfacePersistence = iface.persistence ?? null;
this.newInterfaceSlotTime = iface.slottime ?? null;
this.newInterfacePort = iface.port;
this.newInterfaceRNodeUseIP = false;
if (iface.port && String(iface.port).startsWith("tcp://")) {
const addr = String(iface.port).replace("tcp://", "");
const parts = addr.split(":");
this.newInterfaceRNodeIPHost = parts[0] || "localhost";
this.newInterfaceRNodeIPPort = parts[1] || "7633";
this.newInterfaceRNodeUseIP = true;
}
this.newInterfaceFrequency = iface.frequency;
this.newInterfaceBandwidth = iface.bandwidth;
this.newInterfaceTxpower = iface.txpower;
this.newInterfaceSpreadingFactor = iface.spreadingfactor;
this.newInterfaceCodingRate = iface.codingrate;
this.newInterfaceCommand = iface.command;
this.newInterfaceRespawnDelay = iface.respawn_delay;
this.sharedInterfaceSettings.mode = iface.mode;
this.sharedInterfaceSettings.bitrate = iface.bitrate;
this.sharedInterfaceSettings.network_name = iface.network_name;
this.sharedInterfaceSettings.passphrase = iface.passphrase;
if (iface.frequency) {
this.RNodeGHzValue = Math.floor(iface.frequency / 1e9);
this.RNodeMHzValue = Math.floor((iface.frequency % 1e9) / 1e6);
this.RNodekHzValue = Math.floor((iface.frequency % 1e6) / 1e3);
}
this.discovery.discoverable = this.parseBool(iface.discoverable);
this.discovery.discovery_name = iface.discovery_name ?? "";
this.discovery.announce_interval =
iface.announce_interval != null && iface.announce_interval !== ""
? Number(iface.announce_interval)
: 360;
this.discovery.reachable_on = iface.reachable_on ?? "";
this.discovery.discovery_stamp_value =
iface.discovery_stamp_value != null && iface.discovery_stamp_value !== ""
? Number(iface.discovery_stamp_value)
: 14;
this.discovery.discovery_encrypt = this.parseBool(iface.discovery_encrypt);
this.discovery.publish_ifac = this.parseBool(iface.publish_ifac);
this.discovery.latitude =
iface.latitude != null && iface.latitude !== "" ? Number(iface.latitude) : null;
this.discovery.longitude =
iface.longitude != null && iface.longitude !== "" ? Number(iface.longitude) : null;
this.discovery.height = iface.height != null && iface.height !== "" ? Number(iface.height) : null;
this.discovery.discovery_frequency =
iface.discovery_frequency != null && iface.discovery_frequency !== ""
? Number(iface.discovery_frequency)
: null;
this.discovery.discovery_bandwidth =
iface.discovery_bandwidth != null && iface.discovery_bandwidth !== ""
? Number(iface.discovery_bandwidth)
: null;
this.discovery.discovery_modulation =
iface.discovery_modulation != null && iface.discovery_modulation !== ""
? Number(iface.discovery_modulation)
: null;
} catch (e) {
console.log(e);
}
},
handleRawConfigInput() {
if (!this.rawConfigInput.trim()) {
this.detectedConfigs = [];
return;
}
const configs = [];
const sections = this.rawConfigInput.split(/\[\[(.*?)\]\]/);
// sections[0] is everything before the first [[...]]
for (let i = 1; i Tff7b72< sections.length; i += 2) {
const Te6edf3name = Ta5d6ffsections[i].trim();
const Te6edf3content = Ta5d6ffsections[i + 1] || "";
const Te6edf3config = Ta5d6ff{ name };
// simple key-value extraction
const Te6edf3lines = Ta5d6ffcontent.split("\n");
for (const line of lines) {
const Te6edf3match = Ta5d6ffline.match(/^\s*(\w+)\s*=\s*(.*?)\s*$/);
if (match) {
const Te6edf3key = Ta5d6ffmatch[1].trim();
let Te6edf3value = Ta5d6ffmatch[2].trim();
// clean up quotes if present
if (
(value.startsWith('"') && value.endsWith('"')) ||
(value.startsWith("'") && value.endsWith("'"))
) {
Te6edf3value = Ta5d6ffvalue.substring(1, value.length - 1);
}
config[key] = value;
}
}
if (config.type) {
configs.push(config);
}
}
Te6edf3this.detectedConfigs = Ta5d6ffconfigs;
// if only one config, auto-import it directly
if (Te6edf3configs.length =Ta5d6ff== 1) {
this.quickAddInterfaceFromConfig(configs[0]);
}
},
applyConfig(config) {
if (!config) return;
Te6edf3this.newInterfaceName = Ta5d6ffconfig.name || this.newInterfaceName;
Te6edf3this.newInterfaceType = Ta5d6ffconfig.type;
// Map raw config keys to component data
if (config.target_host) Te6edf3this.newInterfaceTargetHost = Ta5d6ffconfig.target_host;
if (config.target_port) Te6edf3this.newInterfaceTargetPort = Ta5d6ffNumber(config.target_port);
if (config.listen_ip) Te6edf3this.newInterfaceListenIp = Ta5d6ffconfig.listen_ip;
if (config.listen_port) Te6edf3this.newInterfaceListenPort = Ta5d6ffNumber(config.listen_port);
if (config.forward_ip) Te6edf3this.newInterfaceForwardIp = Ta5d6ffconfig.forward_ip;
if (config.forward_port) Te6edf3this.newInterfaceForwardPort = Ta5d6ffNumber(config.forward_port);
if (config.port) {
Te6edf3this.newInterfacePort = Ta5d6ffconfig.port;
if (config.port.startsWith("tcp://")) {
const Te6edf3addr = Ta5d6ffconfig.port.replace("tcp://", "");
const [host, port] = addr.split(":");
Te6edf3this.newInterfaceRNodeIPHost = Ta5d6ffhost;
Te6edf3this.newInterfaceRNodeIPPort = Ta5d6ffport || "7633";
Te6edf3this.newInterfaceRNodeUseIP = Ta5d6fftrue;
}
}
// Radio params
if (config.frequency) {
const Te6edf3freq = Ta5d6ffNumber(config.frequency);
Te6edf3this.RNodeGHzValue = Ta5d6ffMath.floor(freq / 1e9);
Te6edf3this.RNodeMHzValue = Ta5d6ffMath.floor((freq % 1e9) / 1e6);
Te6edf3this.RNodekHzValue = Ta5d6ffMath.floor((freq % 1e6) / 1e3);
}
if (config.bandwidth) Te6edf3this.newInterfaceBandwidth = Ta5d6ffNumber(config.bandwidth);
if (config.txpower) Te6edf3this.newInterfaceTxpower = Ta5d6ffNumber(config.txpower);
if (config.spreadingfactor) Te6edf3this.newInterfaceSpreadingFactor = Ta5d6ffNumber(config.spreadingfactor);
if (config.codingrate) Te6edf3this.newInterfaceCodingRate = Ta5d6ffNumber(config.codingrate);
// KISS/AX.25
if (config.callsign) Te6edf3this.newInterfaceCallsign = Ta5d6ffconfig.callsign;
if (config.ssid) Te6edf3this.newInterfaceSSID = Ta5d6ffconfig.ssid;
if (config.id_callsign) Te6edf3this.newInterfaceIDCallsign = Ta5d6ffconfig.id_callsign;
if (config.id_interval) Te6edf3this.newInterfaceIDInterval = Ta5d6ffNumber(config.id_interval);
if (config.flow_control !== undefined) Te6edf3this.newInterfaceFlowControl = Ta5d6ffthis.parseBool(config.flow_control);
if (config.preamble) Te6edf3this.newInterfacePreamble = Ta5d6ffNumber(config.preamble);
if (config.txtail) Te6edf3this.newInterfaceTXTail = Ta5d6ffNumber(config.txtail);
if (config.persistence) Te6edf3this.newInterfacePersistence = Ta5d6ffNumber(config.persistence);
if (config.slottime) Te6edf3this.newInterfaceSlotTime = Ta5d6ffNumber(config.slottime);
if (config.speed) Te6edf3this.newInterfaceSpeed = Ta5d6ffNumber(config.speed);
if (config.databits) Te6edf3this.newInterfaceDatabits = Ta5d6ffNumber(config.databits);
if (config.parity) Te6edf3this.newInterfaceParity = Ta5d6ffconfig.parity;
if (config.stopbits) Te6edf3this.newInterfaceStopbits = Ta5d6ffNumber(config.stopbits);
// AutoInterface
if (config.group_id) Te6edf3this.newInterfaceGroupID = Ta5d6ffconfig.group_id;
if (config.discovery_scope) Te6edf3this.newInterfaceDiscoveryScope = Ta5d6ffconfig.discovery_scope;
if (config.discovery_port) Te6edf3this.newInterfaceDiscoveryPort = Ta5d6ffNumber(config.discovery_port);
if (config.data_port) Te6edf3this.newInterfaceDataPort = Ta5d6ffNumber(config.data_port);
if (config.multicast_address_type) Te6edf3this.newInterfaceMulticastAddressType = Ta5d6ffconfig.multicast_address_type;
if (config.devices) Te6edf3this.newInterfaceDevices = Ta5d6ffconfig.devices;
if (config.ignored_devices) Te6edf3this.newInterfaceIgnoredDevices = Ta5d6ffconfig.ignored_devices;
if (config.configured_bitrate) Te6edf3this.newInterfaceConfiguredBitrate = Ta5d6ffNumber(config.configured_bitrate);
// TCP / Network extras
if (config.kiss_framing !== undefined)
Te6edf3this.newInterfaceKISSFramingEnabled = Ta5d6ffthis.parseBool(config.kiss_framing);
if (config.i2p_tunneled !== undefined)
Te6edf3this.newInterfaceI2PTunnelingEnabled = Ta5d6ffthis.parseBool(config.i2p_tunneled);
if (config.connect_timeout) Te6edf3this.newInterfaceConnectTimeout = Ta5d6ffNumber(config.connect_timeout);
if (config.max_reconnect_tries) Te6edf3this.newInterfaceMaxReconnectTries = Ta5d6ffNumber(config.max_reconnect_tries);
if (config.fixed_mtu) Te6edf3this.newInterfaceFixedMTU = Ta5d6ffNumber(config.fixed_mtu);
if (config.device) Te6edf3this.newInterfaceNetworkDevice = Ta5d6ffconfig.device;
if (config.prefer_ipv6 !== undefined) Te6edf3this.newInterfacePreferIPV6 = Ta5d6ffthis.parseBool(config.prefer_ipv6);
if (config.connectable !== undefined) Te6edf3this.newInterfaceConnectable = Ta5d6ffthis.parseBool(config.connectable);
// RNode airtime / id callsign
if (config.airtime_limit_long) Te6edf3this.newInterfaceAirtimeLimitLong = Ta5d6ffNumber(config.airtime_limit_long);
if (config.airtime_limit_short) Te6edf3this.newInterfaceAirtimeLimitShort = Ta5d6ffNumber(config.airtime_limit_short);
// Advanced
if (config.mode) Te6edf3this.sharedInterfaceSettings.mode = Ta5d6ffconfig.mode;
if (config.bitrate) Te6edf3this.sharedInterfaceSettings.bitrate = Ta5d6ffNumber(config.bitrate);
if (config.network_name) Te6edf3this.sharedInterfaceSettings.network_name = Ta5d6ffconfig.network_name;
if (config.passphrase) Te6edf3this.sharedInterfaceSettings.passphrase = Ta5d6ffconfig.passphrase;
if (config.discoverable !== undefined && config.discoverable !== null && config.discoverable !== "") {
Te6edf3this.discovery.discoverable = Ta5d6ffthis.parseBool(config.discoverable);
}
if (config.discovery_name) Te6edf3this.discovery.discovery_name = Ta5d6ffconfig.discovery_name;
if (config.announce_interval) Te6edf3this.discovery.announce_interval = Ta5d6ffNumber(config.announce_interval);
if (config.reachable_on) Te6edf3this.discovery.reachable_on = Ta5d6ffconfig.reachable_on;
if (config.discovery_stamp_value)
Te6edf3this.discovery.discovery_stamp_value = Ta5d6ffNumber(config.discovery_stamp_value);
if (config.discovery_encrypt !== undefined)
Te6edf3this.discovery.discovery_encrypt = Ta5d6ffthis.parseBool(config.discovery_encrypt);
if (config.publish_ifac !== undefined) Te6edf3this.discovery.publish_ifac = Ta5d6ffthis.parseBool(config.publish_ifac);
if (config.latitude) Te6edf3this.discovery.latitude = Ta5d6ffNumber(config.latitude);
if (config.longitude) Te6edf3this.discovery.longitude = Ta5d6ffNumber(config.longitude);
if (config.height) Te6edf3this.discovery.height = Ta5d6ffNumber(config.height);
ToastUtils.success(`Imported configuration for "${config.name}"`);
// clear input if applied
Te6edf3this.rawConfigInput = Ta5d6ff"";
Te6edf3this.detectedConfigs = Ta5d6ff[];
},
buildPayloadFromImportedConfig(config) {
const Te6edf3discoveryEnabled =
Ta5d6ffconfig.discoverable !== undefined && config.discoverable !== null && config.discoverable !== ""
? this.parseBool(config.discoverable)
: false;
const Te6edf3i2pPeers =
Ta5d6ffconfig.type === "I2PInterface"
? Array.isArray(config.i2p_peers)
? config.i2p_peers.map((p) =Tff7b72> String(p).trim()).filter(Boolean)
: Array.isArray(config.peers)
? config.peers.map((p) => String(p).trim()).filter(Boolean)
: []
: undefined;
return {
allow_overwriting_interface: false,
name: config.name,
type: config.type,
target_host: config.target_host || config.remote || null,
target_port: this.numOrNull(config.target_port),
transport_identity: config.transport_identity || null,
peers: i2pPeers,
listen_ip: config.listen_ip || null,
listen_port: this.numOrNull(config.listen_port),
port: config.port || null,
frequency: this.numOrNull(config.frequency),
bandwidth: this.numOrNull(config.bandwidth),
txpower: this.numOrNull(config.txpower),
spreadingfactor: this.numOrNull(config.spreadingfactor),
codingrate: this.numOrNull(config.codingrate),
command: config.command || null,
respawn_delay: this.numOrNull(config.respawn_delay),
discoverable: discoveryEnabled ? "yes" : null,
discovery_name: discoveryEnabled ? config.discovery_name || config.name || null : null,
announce_interval: discoveryEnabled ? (this.numOrNull(config.announce_interval) ?? 360) : null,
reachable_on: discoveryEnabled ? config.reachable_on || config.target_host || null : null,
discovery_stamp_value: discoveryEnabled ? (this.numOrNull(config.discovery_stamp_value) ?? 14) : null,
discovery_encrypt: discoveryEnabled
? config.discovery_encrypt !== undefined
? this.parseBool(config.discovery_encrypt)
: false
: null,
publish_ifac: discoveryEnabled
? config.publish_ifac !== undefined
? this.parseBool(config.publish_ifac)
: false
: null,
latitude: discoveryEnabled ? this.numOrNull(config.latitude) : null,
longitude: discoveryEnabled ? this.numOrNull(config.longitude) : null,
height: discoveryEnabled ? this.numOrNull(config.height) : null,
discovery_frequency: discoveryEnabled ? this.numOrNull(config.discovery_frequency) : null,
discovery_bandwidth: discoveryEnabled ? this.numOrNull(config.discovery_bandwidth) : null,
discovery_modulation: discoveryEnabled ? this.numOrNull(config.discovery_modulation) : null,
mode: config.mode || null,
bitrate: this.numOrNull(config.bitrate),
network_name: config.network_name || null,
passphrase: config.passphrase || null,
forward_ip: config.forward_ip || null,
forward_port: this.numOrNull(config.forward_port),
device: config.device || null,
prefer_ipv6:
config.prefer_ipv6 !== undefined && config.prefer_ipv6 !== null && config.prefer_ipv6 !== ""
? this.parseBool(config.prefer_ipv6)
: null,
kiss_framing:
config.kiss_framing !== undefined && config.kiss_framing !== null && config.kiss_framing !== ""
? this.parseBool(config.kiss_framing)
: null,
i2p_tunneled:
config.i2p_tunneled !== undefined && config.i2p_tunneled !== null && config.i2p_tunneled !== ""
? this.parseBool(config.i2p_tunneled)
: null,
connect_timeout: this.numOrNull(config.connect_timeout),
max_reconnect_tries: this.numOrNull(config.max_reconnect_tries),
fixed_mtu: this.numOrNull(config.fixed_mtu),
connectable:
config.type === "I2PInterface"
? config.connectable !== undefined && config.connectable !== null && config.connectable !== ""
? this.parseBool(config.connectable)
: true
: null,
group_id: config.group_id || null,
multicast_address_type: config.multicast_address_type || null,
devices: config.devices || null,
ignored_devices: config.ignored_devices || null,
discovery_scope: config.discovery_scope || null,
discovery_port: this.numOrNull(config.discovery_port),
data_port: this.numOrNull(config.data_port),
configured_bitrate: this.numOrNull(config.configured_bitrate),
callsign: config.callsign || null,
id_callsign: config.id_callsign || null,
id_interval: this.numOrNull(config.id_interval),
ssid: this.numOrNull(config.ssid),
airtime_limit_long: this.numOrNull(config.airtime_limit_long),
airtime_limit_short: this.numOrNull(config.airtime_limit_short),
speed: this.numOrNull(config.speed),
databits: this.numOrNull(config.databits),
parity: config.parity || null,
stopbits: this.numOrNull(config.stopbits),
preamble: this.numOrNull(config.preamble),
txtail: this.numOrNull(config.txtail),
persistence: this.numOrNull(config.persistence),
slottime: this.numOrNull(config.slottime),
flow_control:
config.flow_control !== undefined && config.flow_control !== null && config.flow_control !== ""
? this.parseBool(config.flow_control)
: null,
};
},
applyDiscoveredInterfacePrefill() {
let prefill = null;
try {
if (typeof sessionStorage !== "undefined") {
const raw = sessionStorage.getItem("meshchatx.discoveredInterfacePrefill");
if (raw) {
prefill = JSON.parse(raw);
sessionStorage.removeItem("meshchatx.discoveredInterfacePrefill");
}
}
} catch (e) {
console.log(e);
}
if (!prefill) return;
if (prefill.config_entry) {
this.rawConfigInput = prefill.config_entry;
this.handleRawConfigInput();
return;
}
const config = {
name: prefill.name || "Discovered Interface",
type: prefill.type || "BackboneInterface",
target_host: prefill.target_host || null,
target_port: prefill.target_port != null ? String(prefill.target_port) : null,
transport_identity: prefill.transport_identity || null,
network_name: prefill.network_name || null,
passphrase: prefill.passphrase || null,
frequency: prefill.frequency ?? null,
bandwidth: prefill.bandwidth ?? null,
spreadingfactor: prefill.spreadingfactor ?? null,
codingrate: prefill.codingrate ?? null,
latitude: prefill.latitude ?? null,
longitude: prefill.longitude ?? null,
height: prefill.height ?? null,
};
this.applyConfig(config);
ToastUtils.success(this.$t("interfaces.discovered_prefill_applied"));
},
async quickAddInterfaceFromConfig(config) {
if (!config || !config.type || !config.name || this.isSaving) {
return;
}
this.isSaving = true;
try {
const response = await window.api.post(
`/api/v1/reticulum/interfaces/add`,
this.buildPayloadFromImportedConfig(config)
);
ToastUtils.success(response.data?.message || `Imported interface "${config.name}"`);
GlobalState.hasPendingInterfaceChanges = true;
GlobalState.modifiedInterfaceNames.add(config.name);
this.rawConfigInput = "";
this.detectedConfigs = [];
this.$router.push({ name: "interfaces" });
} catch (e) {
const message = e.response?.data?.message ?? `Failed to import "${config.name}"`;
ToastUtils.error(message);
console.log(e);
} finally {
this.isSaving = false;
}
},
async saveInterface() {
if (this.isSaving) return;
this.isSaving = true;
try {
const discoveryEnabled = this.discovery.discoverable === true;
const freqHz = this.calculateFrequencyInHz();
const i2pPeers =
this.newInterfaceType === "I2PInterface"
? (this.I2PSettings.newInterfacePeers || []).map((p) => String(p).trim()).filter(Boolean)
: undefined;
const isBackboneListener =
this.newInterfaceType === "BackboneInterface" && this.newInterfaceBackboneListenMode === true;
const payload = {
allow_overwriting_interface: this.isEditingInterface,
name: this.newInterfaceName,
type: this.newInterfaceType,
target_host: isBackboneListener ? null : this.newInterfaceTargetHost,
target_port: isBackboneListener ? null : this.newInterfaceTargetPort,
transport_identity: isBackboneListener ? null : this.newInterfaceTransportIdentity,
peers: i2pPeers,
listen_ip: isBackboneListener
? this.newInterfaceBackboneListenIp || null
: this.newInterfaceListenIp,
listen_port: isBackboneListener
? this.newInterfaceBackboneListenPort || null
: this.newInterfaceListenPort,
forward_ip: this.newInterfaceForwardIp,
forward_port: this.newInterfaceForwardPort,
device: isBackboneListener
? this.newInterfaceBackboneListenDevice || null
: this.newInterfaceNetworkDevice,
prefer_ipv6: this.newInterfacePreferIPV6 === true,
kiss_framing: this.newInterfaceKISSFramingEnabled === true,
i2p_tunneled: this.newInterfaceI2PTunnelingEnabled === true,
connect_timeout: this.numOrNull(this.newInterfaceConnectTimeout),
max_reconnect_tries: this.numOrNull(this.newInterfaceMaxReconnectTries),
fixed_mtu: this.numOrNull(this.newInterfaceFixedMTU),
connectable:
this.newInterfaceType === "I2PInterface" ? this.newInterfaceConnectable === true : null,
port: this.newInterfaceRNodeUseIP
? `tcp://${this.newInterfaceRNodeIPHost}:${this.newInterfaceRNodeIPPort}`
: this.newInterfacePort,
frequency: freqHz,
bandwidth: this.newInterfaceBandwidth,
txpower: this.newInterfaceTxpower,
spreadingfactor: this.newInterfaceSpreadingFactor,
codingrate: this.newInterfaceCodingRate,
flow_control: this.newInterfaceFlowControl === true,
callsign: this.newInterfaceCallsign,
id_callsign: this.newInterfaceIDCallsign,
id_interval: this.numOrNull(this.newInterfaceIDInterval),
airtime_limit_long: this.numOrNull(this.newInterfaceAirtimeLimitLong),
airtime_limit_short: this.numOrNull(this.newInterfaceAirtimeLimitShort),
ssid: this.numOrNull(this.newInterfaceSSID),
speed: this.numOrNull(this.newInterfaceSpeed),
databits: this.numOrNull(this.newInterfaceDatabits),
parity: this.newInterfaceParity,
stopbits: this.numOrNull(this.newInterfaceStopbits),
preamble: this.numOrNull(this.newInterfacePreamble),
txtail: this.numOrNull(this.newInterfaceTXTail),
persistence: this.numOrNull(this.newInterfacePersistence),
slottime: this.numOrNull(this.newInterfaceSlotTime),
group_id: this.newInterfaceGroupID,
multicast_address_type: this.newInterfaceMulticastAddressType,
devices: this.newInterfaceDevices,
ignored_devices: this.newInterfaceIgnoredDevices,
discovery_scope: this.newInterfaceDiscoveryScope,
discovery_port: this.numOrNull(this.newInterfaceDiscoveryPort),
data_port: this.numOrNull(this.newInterfaceDataPort),
configured_bitrate: this.numOrNull(this.newInterfaceConfiguredBitrate),
command: this.newInterfaceCommand,
respawn_delay: this.newInterfaceRespawnDelay,
discoverable: discoveryEnabled ? "yes" : null,
discovery_name: discoveryEnabled ? this.discovery.discovery_name : null,
announce_interval: discoveryEnabled
? (this.numOrNull(this.discovery.announce_interval) ?? 360)
: null,
reachable_on: discoveryEnabled ? this.discovery.reachable_on : null,
discovery_stamp_value: discoveryEnabled
? (this.numOrNull(this.discovery.discovery_stamp_value) ?? 14)
: null,
discovery_encrypt: discoveryEnabled ? this.discovery.discovery_encrypt === true : null,
publish_ifac: discoveryEnabled ? this.discovery.publish_ifac === true : null,
latitude: discoveryEnabled ? this.numOrNull(this.discovery.latitude) : null,
longitude: discoveryEnabled ? this.numOrNull(this.discovery.longitude) : null,
height: discoveryEnabled ? this.numOrNull(this.discovery.height) : null,
discovery_frequency: discoveryEnabled ? this.numOrNull(this.discovery.discovery_frequency) : null,
discovery_bandwidth: discoveryEnabled ? this.numOrNull(this.discovery.discovery_bandwidth) : null,
discovery_modulation: discoveryEnabled ? this.numOrNull(this.discovery.discovery_modulation) : null,
mode: this.sharedInterfaceSettings.mode || null,
bitrate: this.sharedInterfaceSettings.bitrate,
network_name: this.sharedInterfaceSettings.network_name,
passphrase: this.sharedInterfaceSettings.passphrase,
};
const response = await window.api.post(`/api/v1/reticulum/interfaces/add`, payload);
if (response.data.message) ToastUtils.success(response.data.message);
GlobalState.hasPendingInterfaceChanges = true;
GlobalState.modifiedInterfaceNames.add(this.newInterfaceName);
this.$router.push({ name: "interfaces" });
} catch (e) {
const message = e.response?.data?.message ?? "Failed to save interface connection.";
ToastUtils.error(message);
console.log(e);
} finally {
this.isSaving = false;
}
},
calculateFrequencyInHz() {
return this.RNodeGHzValue * 1e9 + this.RNodeMHzValue * 1e6 + this.RNodekHzValue * 1e3;
},
updateRNodeCalculations() {
this.calculateRNodeParameters(
this.newInterfaceBandwidth,
this.newInterfaceSpreadingFactor,
this.newInterfaceCodingRate,
this.RNodeInterfaceLoRaParameters.noiseFloor,
this.RNodeInterfaceLoRaParameters.antennaGain,
this.newInterfaceTxpower
);
},
calculateRNodeParameters(bandwidth, spreadingFactor, codingRate, noiseFloor, antennaGain, transmitPower) {
if (!bandwidth || !spreadingFactor || !codingRate) return;
const crn = { 5: 1, 6: 2, 7: 3, 8: 4 };
const cr = crn[codingRate];
const sfn = { 5: -2.5, 6: -5, 7: -7.5, 8: -10, 9: -12.5, 10: -15, 11: -17.5, 12: -20 };
let dataRate =
spreadingFactor * (4 / (4 + cr) / (Math.pow(2, spreadingFactor) / (bandwidth / 1000))) * 1000;
let sensitivity = -174 + 10 * Math.log10(bandwidth) + noiseFloor + (sfn[spreadingFactor] || 0);
if (bandwidth === 203125 || bandwidth === 406250 || bandwidth > 500000) {
sensitivity = -165.6 + 10 * Math.log10(bandwidth) + noiseFloor + (sfn[spreadingFactor] || 0);
}
let linkBudget = transmitPower - sensitivity + antennaGain;
this.RNodeInterfaceLoRaParameters.dataRate =
dataRate Tff7b72< 1000 ? `${dataRate.toFixed(0)} bps` : `${(dataRate / 1000).toFixed(2)} kbps`;
Te6edf3this.RNodeInterfaceLoRaParameters.linkBudget = Ta5d6ff`${linkBudget.toFixed(1)} dB`;
Te6edf3this.RNodeInterfaceLoRaParameters.sensitivity = Ta5d6ff`${sensitivity.toFixed(1)} dBm`;
},
addI2PPeer(Te6edf3address = Ta5d6ff"") {
this.I2PSettings.newInterfacePeers.push(address);
},
removeI2PPeer(index) {
this.I2PSettings.newInterfacePeers.splice(index, 1);
},
addSubInterface() {
this.RNodeMultiInterface.subInterfaces.push({
name: "",
frequency: null,
bandwidth: null,
txpower: null,
spreadingfactor: null,
codingrate: null,
vport: null,
});
},
useKISSAX25() {
Te6edf3this.newInterfaceType =
Ta5d6ffthis.newInterfaceType === "AX25KISSInterface" ? "KISSInterface" : "AX25KISSInterface";
},
removeSubInterface(idx) {
this.RNodeMultiInterface.subInterfaces.splice(idx, 1);
},
},
};
</scriptTff7b72>
Tff7b72<style scopedTff7b72>
.glass-card {
@apply bg-white/95 dark:bg-zinc-900/85 backdrop-blur border border-gray-200 dark:border-zinc-800 rounded-3xl shadow-xl p-6;
}
.input-field {
@apply bg-gray-50/90 dark:bg-zinc-900/80 border border-gray-200 dark:border-zinc-700 text-sm rounded-2xl focus:ring-2 focus:ring-blue-400 focus:border-blue-400 dark:focus:ring-blue-500 dark:focus:border-blue-500 block w-full p-2.5 text-gray-900 dark:text-gray-100 transition;
}
.glass-label {
@apply mb-1.5 block text-xs uppercase font-bold text-gray-500 dark:text-zinc-400 tracking-wider;
}
.glass-field {
@apply space-y-1;
}
Tff7b72</style>
Served by rngit 1.5.2 - Generated in 0.17s